ArchR takes as input aligned BAM or fragment files. These files are stored in the HDF5 file format (hierarchical data format version5). The HDF5 files are the constituent pieces of an ArchR analysis. They are called Arrow files. All Arrow files are grouped into a project, a compressed R data file. The Files are accessed in minimal chunks (parallel read and write operations). Therefore, in memory we do not have any large file sizes.
To select high quality cells TSS enrichment scores are used.
First steps in ArchR
Load libraries
library(ArchR)
library(knitr)
library(rhdf5)
#library(caret)
h5disableFileLocking()
inputFiles <- getTutorialData("Hematopoiesis")
addArchRGenome("hg19")
Create Arrow Files
- read accessible fragments
- calculate QC information for each cell (TSS enrichment scores and nucelosome info)
- filter cells based on QC parameters
- create genome-wide tile matrix using 500-bp bins
- create GeneScoreMatrix using gene annotation
TSS enrichment score
= signal to noise calculation The reads around a particular TSS are collected and aggregated to form a distribution of reads which is centered on th TSS. this distribution extends to 1000bp in both directions * we take the average read depth in the 100 bps at each end flank and calcualte a fold change at each position over that average read depth (total of 200bp of averaged data) * If there is a high read signal at TSS there will be an increase in signal towards the middle of the distribution * high signal means that the region is accessible * the signal value at the center of the distribution is the TSS enrichment metric
ArrowFiles <- createArrowFiles(
inputFiles = inputFiles,
sampleNames = names(inputFiles),
minTSS = 4, #Dont set this too high because you can always increase later
minFrags = 1000, # minimum number of mapped fragments required
addTileMat = TRUE,
addGeneScoreMat = TRUE,
subThreading = FALSE
)
# the arrow files object simply contains a character vector of arrow file paths
ArrowFiles
## [1] "scATAC_BMMC_R1.arrow" "scATAC_CD34_BMMC_R1.arrow"
## [3] "scATAC_PBMC_R1.arrow"
Quality Control
- the number of unique nuclear fragments (as opposed to mitochondrial fragments)
- signal-to-background ratio -> if this is low this probably corresponds to dying cells where the entire genome allows random transposition
- fragment size distribution -> since 147 bp are wrapped around a nucleosome it is expected that there are depletions of fragments of this length at regular intervals
Inferring Doublets
Should be removed, because they can interfere with downstream analysis.
Doublet detection-and-removal algorithm: Heterotypic doublets are identified by generating a collection of synthetic doublets. These synthetic doublets are projected into low-dimensional embeddings. Searching for nearest neighbours to the synthetic doublets we can identify doublets in the dataset. This outperforms the prediction of doublets using fragment number (ROC-AUC). (Compared to demuxlet as ground truth)
doubScores <- addDoubletScores(
input = ArrowFiles,
k = 10, #Refers to how many cells near a "pseudo-doublet" to count.
knnMethod = "UMAP", #Refers to the embedding to use for nearest neighbor search.
LSIMethod = 1
)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
Create ArchRProject
An ArchR Project is initialized with some important attributes:
- ouput directory
- sample names
sampleColData -> matrix containint data for each sample
cellColData -> contains data associated with each cell
- after using
addDoubletScore() there will be a column for “Doublet Enrichment” and “Doublet Score”
- total number of cells (excluding doublets)
- median TSS score & median number of fragments across all cells and samples
proj <- ArchRProject(
ArrowFiles = ArrowFiles,
outputDirectory = "ArchRVignette",
copyArrows = TRUE#This is recommened so that you maintain an unaltered copy for later usage.
)
Access ArchR:
Cell Names:
# cell names
proj$cellNames[1:5]
## [1] "scATAC_BMMC_R1#TTATGTCAGTGATTAG-1" "scATAC_BMMC_R1#AAGATAGTCACCGCGA-1"
## [3] "scATAC_BMMC_R1#GCATTGAAGATTCCGT-1" "scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1"
## [5] "scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1"
Sample Names:
proj$Sample[1:5]
## [1] "scATAC_BMMC_R1" "scATAC_BMMC_R1" "scATAC_BMMC_R1" "scATAC_BMMC_R1"
## [5] "scATAC_BMMC_R1"
length(proj$Sample)
## [1] 10660
For each cell there is a TSS enrichment score:
proj$TSSEnrichment %>% head
## [1] 7.204 7.949 4.447 6.941 4.771 9.185
quantile(proj$TSSEnrichment)
## 0% 25% 50% 75% 100%
## 4.1090 13.9255 16.8150 19.9285 41.9800
How to subset an ArchR project
# note that that only 100 cells are accessed
proj[1:100,]
## class: ArchRProject
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
## BlacklistRatio
## numberOfCells(1): 100
## medianTSS(1): 10.794
## medianFrags(1): 10200.5
proj[proj$cellNames[1:100], ]
## class: ArchRProject
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
## BlacklistRatio
## numberOfCells(1): 100
## medianTSS(1): 10.794
## medianFrags(1): 10200.5
idxSample <- BiocGenerics::which(proj$Sample %in% "scATAC_BMMC_R1")
cellsSample <- proj$cellNames[idxSample]
proj[cellsSample, ]
## class: ArchRProject
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
## BlacklistRatio
## numberOfCells(1): 4932
## medianTSS(1): 15.2575
## medianFrags(1): 2771
- specific cutoff for TSS enrichment score
idxPass <- which(proj$TSSEnrichment >= 8)
cellsPass <- proj$cellNames[idxPass]
proj[cellsPass, ]
## class: ArchRProject
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
## BlacklistRatio
## numberOfCells(1): 10499
## medianTSS(1): 16.899
## medianFrags(1): 3042
# What information does the vector of TSS enrichment contain?
class(proj$TSSEnrichment)
## [1] "numeric"
length(proj$TSSEnrichment)
## [1] 10660
length(proj$nFrags)
## [1] 10660
getAvailableMatrices(proj)
## [1] "GeneScoreMatrix" "TileMatrix"
Obtaining columns from cellColData
- retrieve metadata columns, e.g. number of unique nuclear fragments per cell
df <- getCellColData(proj, select = "nFrags")
df %>% head %>% kable()
| scATAC_BMMC_R1#TTATGTCAGTGATTAG-1 |
26189 |
| scATAC_BMMC_R1#AAGATAGTCACCGCGA-1 |
20648 |
| scATAC_BMMC_R1#GCATTGAAGATTCCGT-1 |
18991 |
| scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1 |
18296 |
| scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1 |
17458 |
| scATAC_BMMC_R1#AGTTACGAGAACGTCG-1 |
17109 |
- operations on a given column
df <- getCellColData(proj, select = c("log10(nFrags)", "nFrags -1 "))
df %>% head %>% kable()
| scATAC_BMMC_R1#TTATGTCAGTGATTAG-1 |
4.418119 |
26188 |
| scATAC_BMMC_R1#AAGATAGTCACCGCGA-1 |
4.314878 |
20647 |
| scATAC_BMMC_R1#GCATTGAAGATTCCGT-1 |
4.278548 |
18990 |
| scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1 |
4.262356 |
18295 |
| scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1 |
4.241994 |
17457 |
| scATAC_BMMC_R1#AGTTACGAGAACGTCG-1 |
4.233225 |
17108 |
Plot QC metrics
-log10(unique fragments) vs TSS enrichment
- TSS enrichment score = signa-to-background
- number of unique fragments -> cells with very few fragments do not have enough data to confidently analyze them
- in the plot areas with more points/cells are colored in orange, and areas with less points in blue, indicating the distribution of cell
df <- getCellColData(proj, select = c("log10(nFrags)", "TSSEnrichment"))
df %>% head
## DataFrame with 6 rows and 2 columns
## log10(nFrags) TSSEnrichment
## <numeric> <numeric>
## scATAC_BMMC_R1#TTATGTCAGTGATTAG-1 4.41812 7.204
## scATAC_BMMC_R1#AAGATAGTCACCGCGA-1 4.31488 7.949
## scATAC_BMMC_R1#GCATTGAAGATTCCGT-1 4.27855 4.447
## scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1 4.26236 6.941
## scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1 4.24199 4.771
## scATAC_BMMC_R1#AGTTACGAGAACGTCG-1 4.23322 9.185
p <- ggPoint(
x = df[, 1], y = df[, 2],
colorDensity = TRUE, # should the density of points on the plot be indicated by color?
continuousSet = "sambaNight",
xlabel = "Log10 unique fragments",
ylabel = "TSS enrichment",
xlim = c(log10(500), quantile(df[,1], probs = 0.99)),
ylim = c(0, quantile(df[,2], probs = 0.99))
) + geom_hline(yintercept = 4, lty = "dashed") +
geom_vline(xintercept = 3, lty = "dashed")
p

Plotting sample statistics
- when we have distinct samples, it can be important to compare various metric between samples
- ridge plots & violin plots are used for grouped data
p1 <- plotGroups(
ArchRProj = proj,
groupBy = "Sample",
colorBy = "cellColData",
name = "TSSEnrichment",
plotAs = "ridges"
)
## Warning: `expand_scale()` is deprecated; use `expansion()` instead.
p2 <- plotGroups(
ArchRProj = proj,
groupBy = "Sample",
colorBy = "cellColData",
name = "TSSEnrichment",
plotAs = "violin",
alpha = 0.4,
addBoxPlot = TRUE
)
ggAlignPlots(p1, p2, type = "h")
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.

p1 <- plotGroups(
ArchRProj = proj,
groupBy = "Sample",
colorBy = "cellColData",
name = "log10(nFrags)",
plotAs = "ridges"
)
## Warning: `expand_scale()` is deprecated; use `expansion()` instead.
p2 <- plotGroups(
ArchRProj = proj,
groupBy = "Sample",
colorBy = "cellColData",
name = "log10(nFrags)",
plotAs = "violin",
alpha= 0.4,
addBoxPlot = TRUE
)
ggAlignPlots(p1, p2, type = "h")
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.

Plot Fragment Size Distribution & TSS Enrichment Profiles
- the distribution of fragments size can be very different between samples, cell types and batches -> these differences do not necessarily correlate with differences in quality
- the dip is the fragment size of a nucleosome ~147bp
- TSS enrichment profiles
- clear peak in the center
- smaller shoulder peak right of the center caused by well positioned +1 nucleosome
p1 <- plotFragmentSizes(ArchRProj = proj)
p2 <- plotTSSEnrichment(ArchRProj = proj)
ggAlignPlots(p1, p2, type = "v")

Filtering Doublets
With the function addDoubleScores() information on predicted doublets has been added. Filter the putative doublets. They are not removed physically, but excluded from downstream analysis. ArchR automatically prints the number of cells removed from each sample and the corresponding percentage which is very handy.
arguments:
- cutEnrich = minimum cutoff for DoubletEnrichment, number of simulated doublets divided by expected number given a random uniform distribution
- cutScore = minimum cutoff for Doublet Score, represents -log10(binomial adjusted p-value) for the DoubletEnrichmentadd
- filteRatio = maximum ratio of predicted doublets to filter based on number of pass-filter cells (A higher filterRatio means that more cells are removed) e.g. 5000 cells
maximum would be filterRatio * 5000 / 100000 = filterRatio * 5000 * 0.05
This way samples with different percentage of doublets will be filtered accordingly.
# in our case we now have 10 251 cells as opposed to 10 661 cells before
# filtering -> 410 cells were removed (3.85%)
proj <- filterDoublets(ArchRProj = proj)
Dimensionality reduction & Clustering
- two other algorithms:
- latent semantic indexing (LSI) in Signac
- landmark diffusion maps (LDM) in SnapATAC
- ArchR: optimized iterative LSI method
- exhibits less susceptibility to batch effects
- focuses on most variable features
- create a LSI REduction from a subset of the total cells
- linearly project the remaining cells into this subspace with LSI proejection (based on SVD)
Because we can have maximally two accessible alleles per cell, the scATAC-seq data is sparse. Therefore, the majority of accessible regions are not transposed, meaning that most loci will have 0 accessible alleles. A zero could mean “non-accessible” or “not sampled”. For many analysis we can use a binarized matix. Imporantly, the 1s have information, BUT the 0s do not!
A PCA would result in high inter-cell similarity at all 0 positions. An alternative approach for dimensionality reduction is a layered dimensionality reduction. First, Latent Semantic Indexing (LSI) is used. LSI is an approach from language processing. Different samples are the “documents” and different regions/peaks are the “words”.
Iterative LSI
- compute term frequency (depth normalization to 10,000 per single cell)
\(TF = \frac{C_{ij}}{F_{j}}\) with \(C_{ij}\) being the total number of counts for peak i in cell j and \(F_{j}\) being the total number of counts in cell j.
- Inverse document frequency
- weights features by how often they occur
- more weight to less frequent peaks
\(IDF = \frac{N}{n_{p}}\) with N being the total number of cells in the dataset and \(n_{p}\) being the total number of coutns for peak i across all cells.
- The term frequency TF is normalized by the inverse document frequncy IDF. You get a TF-IDF matrix (term frequency-inverse document frequency) which tells us how important a region/peak is to a sample. In other words you transform a binary matrix to a non-binary matrix.
\(TF-IDF = \log{1 + (TF * IDF) 10^{4}}\)
- SVD identifies the most valuable information across samples. Then we can use these most valuable features to represent the data in a lower dimensional space
- Clusters are identified with Seurat’s Shared Nearest Neighbor clustering
- Sum accessibility across all single cells in each cluster -> log-normalize
- Identify most variable features across the clusters
- repeat with most variable peaks as features
With LSI we can reduce the dimensionality of the sparse insertion matrix to tens or hundreds. Then UMAP or t-SNE can be used to visualize the data
Unlike in scRNA-seq we cannot select the top highly variable features before dimensionality reduction (high noise, low reproducibility). Rather the iterative LSI approach first computes a LSI on the most accessible tiles (this will identify clusters corresponding to the major cell types). Then, ArchR computes the average accessibility across these clusters across all features. Next, the most variable peaks across these clusters are identified. The most highly accessible peaks are the features of a new round of LSI. We can set how many rounds of LSI we want to be peformed.
Using iterative LSI reduces batch effects. If you see some batch effects you could try to add more LSI iterations and start from a lower intitial clustering resolution. Also, the number of variable features can be lowered.
Estimated LSI
- for extremely large scATAC-seq datasets
- subset of landmark cells (randomly selected) are used for LSI
- remaining cells are TF-IDF normlaized using the IDF determined from the landmark cells from step one
- normalized cells are porjected into the SVD space defined by the landmark cells
The LSI transformation is, therefore, based on a subset of cells only and the remaining cells are projected into this landmark LSI. This approach minimizes memory usage. The landmark set size depends on cell type proportions.
- iterative LSI dimensionality reduction -> explore the parameters!
Batch correct with Harmony
- dimensionality reduction object from ArchR isp passed to Harmony
proj <- addIterativeLSI(ArchRProj = proj, useMatrix = "TileMatrix", name = "IterativeLSI")
proj <- addIterativeLSI(
ArchRProj = proj,
useMatrix = "TileMatrix",
name = "IterativeLSI",
iterations = 2,
clusterParams = list( #See Seurat::FindClusters
resolution = c(0.2),
sampleCells = 10000,
n.start = 10
),
varFeatures = 25000,
dimsToUse = 1:30
)
Clustering
Calling clusters in this new space uses the Seurat’s graph clustering function as default clustering method. The Seurat method first computs KNN graph and then a modularity optimization technique to cluster the cells (iteratively group cells togetether with Louvian algorithm using 10 random starts). Another option is to use “Scran”. The default number of nearest neighbors used is 10. The minimum number of cells for a cluster to be called a cluster is set to 5 by default. The maximum number of clusters to be called is set to 25 by default.
proj <- addClusters(input = proj, reducedDims = "IterativeLSI")
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
##
## Number of nodes: 10250
## Number of edges: 473760
##
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8640
## Number of communities: 13
## Elapsed time: 1 seconds
Visualizing a UMAP embedding
- uses uwot package
- various attributes of the data can be visualized
- these are stored in a matrix called
cellColData
- which variable the plot is colored by is specified by
colorBy and name parameter
proj <- addUMAP(ArchRProj = proj, reducedDims = "IterativeLSI")
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid uid value replaced by that for user 'nobody''
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid gid value replaced by that for user 'nobody''
## Warning: invalid gid value replaced by that for user 'nobody'
# color by sample
p1 <- plotEmbedding(ArchRProj = proj, colorBy = "cellColData", name = "Sample", embedding = "UMAP")
# color by cluster
p2 <- plotEmbedding(ArchRProj = proj, colorBy = "cellColData", name = "Clusters", embedding = "UMAP")
ggAlignPlots(p1, p2, type = "h")

Cluster assignment using gene scores
For the toy dataset marker genes of known hematopoietic regulators can be used. Using MAGIC we add imputation weights to smooth the dropout noise in the gene scores
proj <- addImputeWeights(proj)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes <- c(
"CD34", #Early Progenitor
"GATA1", #Erythroid
"PAX5", "MS4A1", "MME", #B-Cell Trajectory
"CD14", "MPO", #Monocytes
"CD3D", "CD8A"#TCells
)
p <- plotEmbedding(
ArchRProj = proj,
colorBy = "GeneScoreMatrix",
name = markerGenes,
embedding = "UMAP",
imputeWeights = getImputeWeights(proj)
)
p
## $CD34

##
## $GATA1

##
## $PAX5

##
## $MS4A1

##
## $MME

##
## $CD14

##
## $MPO

##
## $CD3D

##
## $CD8A

Visualizing Genome Browser Tracks
Browse local chromatin accessibility at marker genes. Plot genome browser tracks per cluster
p <- plotBrowserTrack(
ArchRProj = proj,
groupBy = "Clusters",
geneSymbol = markerGenes,
upstream = 50000,
downstream = 50000
)
## GRanges object with 9 ranges and 2 metadata columns:
## seqnames ranges strand | gene_id symbol
## <Rle> <IRanges> <Rle> | <character> <character>
## [1] chr1 208059883-208084683 - | 947 CD34
## [2] chrX 48644982-48652717 + | 2623 GATA1
## [3] chr9 36838531-37034476 - | 5079 PAX5
## [4] chr11 60223282-60238225 + | 931 MS4A1
## [5] chr3 154741913-154901518 + | 4311 MME
## [6] chr5 140011313-140013286 - | 929 CD14
## [7] chr17 56347217-56358296 - | 4353 MPO
## [8] chr11 118209789-118213459 - | 915 CD3D
## [9] chr2 87011728-87035519 - | 925 CD8A
## -------
## seqinfo: 24 sequences from hg19 genome
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
grid::grid.newpage()
grid::grid.draw(p$CD14)

saveArchRProject(proj, outputDirectory = "ArchRVignette", load = FALSE)
Integration with scRNA-seq
- the scATAC-seq gene score matrix is compared with the scRNA-seq gene expression matrix
- for this alignment the
FindTransferAnchors() function from Seurat is used
- to scale for large sample size, this process is parallelized
- for each cell in ATAC we find the cell in scRNA-seq that looks most similar -> assign the correpsonding gene expression to that cell.
Apart from using this information for identifying clusters we can also use it for identifying predicted cis-regulatory elements.
if(!file.exists("scRNA-Hematopoiesis-Granja-2019.rds")){
download.file(
url = "https://jeffgranja.s3.amazonaws.com/ArchR/TestData/scRNA-Hematopoiesis-Granja-2019.rds",
destfile = "scRNA-Hematopoiesis-Granja-2019.rds"
)
}
# ranged summarized Experiment
seRNA <- readRDS("scRNA-Hematopoiesis-Granja-2019.rds")
seRNA
## class: RangedSummarizedExperiment
## dim: 20287 35582
## metadata(0):
## assays(1): counts
## rownames(20287): FAM138A OR4F5 ... S100B PRMT2
## rowData names(3): gene_name gene_id exonLength
## colnames(35582): CD34_32_R5:AAACCTGAGTATCGAA-1
## CD34_32_R5:AAACCTGAGTCGTTTG-1 ...
## BMMC_10x_GREENLEAF_REP2:TTTGTTGCATGTGTCA-1
## BMMC_10x_GREENLEAF_REP2:TTTGTTGCATTGAAAG-1
## colData names(10): Group nUMI_pre ... BioClassification Barcode
table(colData(seRNA)$BioClassification)
##
## 01_HSC 02_Early.Eryth 03_Late.Eryth 04_Early.Baso 05_CMP.LMPP
## 1425 1653 446 111 2260
## 06_CLP.1 07_GMP 08_GMP.Neut 09_pDC 10_cDC
## 903 2097 1050 544 325
## 11_CD14.Mono.1 12_CD14.Mono.2 13_CD16.Mono 14_Unk 15_CLP.2
## 1800 4222 292 520 377
## 16_Pre.B 17_B 18_Plasma 19_CD8.N 20_CD4.N1
## 710 1711 62 1521 2470
## 21_CD4.N2 22_CD4.M 23_CD8.EM 24_CD8.CM 25_NK
## 2364 3539 796 2080 2143
## 26_Unk
## 161
Unconstrained & Constrained integration
Unconstrained integration:
- takes all cells of scATAC-seq data and attempts to align them to any of the scRNA-seq cells
- this provides preliminary cluster identities which will serve as prior knowledge for the constrained integration
- the quality can be improved by constraining the integration
The integration matrix will be stored in the ArchR project via matrixName. The other parameters will be saved in the cellColData (nameCell = store matched cell ID from scRNA-seq, nameGroup = store group ID from scRNA-seq, nameScore = store cross-platform integration score)
proj <- addGeneIntegrationMatrix(
ArchRProj = proj,
useMatrix = "GeneScoreMatrix",
matrixName = "GeneIntegrationMatrix",
reducedDims = "IterativeLSI",
seRNA = seRNA,
addToArrow = FALSE,
groupRNA = "BioClassification", # Bioclassification is a column in
# the colData of the seRNA object, this column will be used to
# determine the subgroupings specified in groupList for constrained integration
# it is also used for the nameGroup output of this function
nameCell = "predictedCell_Un", # name the cellColData for the
# predicted scRNA-seq cell in the specified ArchRProject ->
# will add a column to the project
nameGroup = "predictedGroup_Un",
nameScore = "predictedScore_Un"
)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
Constrained integration:
- prior knowledge of the cell types is used to limit the search space of the alignment -> e.g. if we know that clusters A, B, C in scATAC-seq data correspond to 3 different T cell clusters and Clusters X, Y in scRNA-seq data correspond to two T cell clusters, we could try to specifically align these cells
- Identify which cell types from scRNA-seq data are most abundant in scATAC-seq clusters?
- confusion matrix is created that looks at the intersection of Clusters and predicted group which contains the cell types as identfied by scRNA-seq
- This shows which scRNA-seq cell type is most abundant in each of the scATAC clusters
# in the confusion matrix the rows correspond to clusters in scATAC data
# the columns correspond to cell types in scRNA-seq data
cM <- as.matrix(confusionMatrix(proj$Clusters, proj$predictedGroup_Un))
cM[, 1:5]
## 17_B 16_Pre.B 08_GMP.Neut 25_NK 11_CD14.Mono.1
## C4 407 1 0 1 0
## C2 3 330 0 2 0
## C10 3 0 276 0 74
## C6 0 0 0 570 0
## C8 0 0 6 0 1237
## C12 0 0 26 5 0
## C5 0 0 0 4 0
## C11 0 0 751 0 2
## C7 0 0 0 14 0
## C13 0 0 60 2 0
## C1 0 0 0 0 0
## C9 2 0 3 0 366
## C3 0 1 1 0 0
preClust <- colnames(cM)[apply(cM, 1 , which.max)]
cbind(preClust, rownames(cM)) #Assignments
## preClust
## [1,] "17_B" "C4"
## [2,] "16_Pre.B" "C2"
## [3,] "08_GMP.Neut" "C10"
## [4,] "25_NK" "C6"
## [5,] "11_CD14.Mono.1" "C8"
## [6,] "02_Early.Eryth" "C12"
## [7,] "21_CD4.N2" "C5"
## [8,] "08_GMP.Neut" "C11"
## [9,] "22_CD4.M" "C7"
## [10,] "01_HSC" "C13"
## [11,] "09_pDC" "C1"
## [12,] "12_CD14.Mono.2" "C9"
## [13,] "15_CLP.2" "C3"
Which cells in the scRNA-seq correspond to Tcells and NK cells?
Use pattern matching strings in combination with grep to extract the scATAC-seq clusters that correspond to these scRNA-seq cell types. | acts as an or statement, so we search for any row in the preClust column of the confusion matix that matches a scRNA-seq cluster number.
unique(proj$predictedGroup_Un)
## [1] "17_B" "16_Pre.B" "08_GMP.Neut" "25_NK"
## [5] "11_CD14.Mono.1" "07_GMP" "02_Early.Eryth" "24_CD8.CM"
## [9] "05_CMP.LMPP" "03_Late.Eryth" "22_CD4.M" "09_pDC"
## [13] "21_CD4.N2" "15_CLP.2" "01_HSC" "19_CD8.N"
## [17] "12_CD14.Mono.2" "20_CD4.N1" "06_CLP.1" "04_Early.Baso"
## [21] "26_Unk" "23_CD8.EM" "13_CD16.Mono"
cTNK <- paste0(paste0(19:25), collapse = "|")
cTNK
## [1] "19|20|21|22|23|24|25"
cNonTNK <- paste0(c(paste0("0", 1:9), 10:13, 15:18), collapse = "|")
cNonTNK
## [1] "01|02|03|04|05|06|07|08|09|10|11|12|13|15|16|17|18"
clustTNK <- rownames(cM)[grep(cTNK, preClust)]
clustTNK
## [1] "C6" "C5" "C7"
clustNonTNK <- rownames(cM)[grep(cNonTNK, preClust)]
clustNonTNK
## [1] "C4" "C2" "C10" "C8" "C12" "C11" "C13" "C1" "C9" "C3"
Identify scRNA-seq cells corresponding to the same cell types.
#RNA get cells in these categories
rnaTNK <- colnames(seRNA)[grep(cTNK, colData(seRNA)$BioClassification)]
head(rnaTNK)
## [1] "PBMC_10x_GREENLEAF_REP1:AAACCCAGTCGTCATA-1"
## [2] "PBMC_10x_GREENLEAF_REP1:AAACCCATCCGATGTA-1"
## [3] "PBMC_10x_GREENLEAF_REP1:AAACCCATCTCAACGA-1"
## [4] "PBMC_10x_GREENLEAF_REP1:AAACCCATCTCTCGAC-1"
## [5] "PBMC_10x_GREENLEAF_REP1:AAACGAACAATCGTCA-1"
## [6] "PBMC_10x_GREENLEAF_REP1:AAACGAACACGATTCA-1"
# get cell not in these categories
rnaNonTNK <- colnames(seRNA)[grep(cNonTNK, colData(seRNA)$BioClassification)]
head(rnaNonTNK)
## [1] "CD34_32_R5:AAACCTGAGTATCGAA-1" "CD34_32_R5:AAACCTGAGTCGTTTG-1"
## [3] "CD34_32_R5:AAACCTGGTTCCACAA-1" "CD34_32_R5:AAACGGGAGCTTCGCG-1"
## [5] "CD34_32_R5:AAACGGGAGGGAGTAA-1" "CD34_32_R5:AAACGGGAGTTACGGG-1"
We create a nested list with two vectors of cell IDs
groupList <- SimpleList(
TNK = SimpleList(
ATAC = proj$cellNames[proj$Clusters %in% clustTNK],
RNA = rnaTNK
),
NonTNK = SimpleList(
ATAC = proj$cellNames[proj$Clusters %in% clustNonTNK],
RNA = rnaNonTNK
)
)
groupList[[1]][[1]] %>% head
## [1] "scATAC_BMMC_R1#GTAGGAGCATTATGGC-1" "scATAC_BMMC_R1#CTAGGATTCTATGAGC-1"
## [3] "scATAC_BMMC_R1#AGTTTGGCACGCGCAT-1" "scATAC_BMMC_R1#GCAGATTGTATGGGTG-1"
## [5] "scATAC_BMMC_R1#ACATGGTCAGTATCTG-1" "scATAC_BMMC_R1#AGATTCGCATAGTCCA-1"
proj <- addGeneIntegrationMatrix(
ArchRProj = proj,
useMatrix = "GeneScoreMatrix",
matrixName = "GeneIntegrationMatrix",
reducedDims = "IterativeLSI",
seRNA = seRNA,
addToArrow = FALSE,
groupList = groupList,
groupRNA = "BioClassification",
nameCell = "predictedCell_Co",
nameGroup = "predictedGroup_Co",
nameScore = "predictedScore_Co"
)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
pal <- paletteDiscrete(values = colData(seRNA)$BioClassification)
pal
## 01_HSC 02_Early.Eryth 03_Late.Eryth 04_Early.Baso 05_CMP.LMPP
## "#D51F26" "#502A59" "#235D55" "#3D6E57" "#8D2B8B"
## 06_CLP.1 07_GMP 08_GMP.Neut 09_pDC 10_cDC
## "#DE6C3E" "#F9B712" "#D8CE42" "#8E9ACD" "#B774B1"
## 11_CD14.Mono.1 12_CD14.Mono.2 13_CD16.Mono 14_Unk 15_CLP.2
## "#D69FC8" "#C7C8DE" "#8FD3D4" "#89C86E" "#CC9672"
## 16_Pre.B 17_B 18_Plasma 19_CD8.N 20_CD4.N1
## "#CF7E96" "#A27AA4" "#CD4F32" "#6B977E" "#518AA3"
## 21_CD4.N2 22_CD4.M 23_CD8.EM 24_CD8.CM 25_NK
## "#5A5297" "#0F707D" "#5E2E32" "#A95A3C" "#B28D5C"
## 26_Unk
## "#3D3D3D"
Compare unconstrained and constrained integration
Plot the integration for unconstrained (left) and constrained (right) integration. We overlay the scRNA-seq cell types on the ATAC-seq data based on the unconstrained/constrained integration. In the plot below the difference is very subtle, because the cell types of interest are very distinct. There are subtle differences, for example in the T cell clusters. Could we also try this approach with constraining other cell types? Can we combine different constrains/groups?
p1 <- plotEmbedding(
proj,
colorBy = "cellColData",
name = "predictedGroup_Un",
pal = pal
)
p2 <- plotEmbedding(
proj,
colorBy = "cellColData",
name = "predictedGroup_Co",
pal = pal
)
ggAlignPlots(p1, p2, type = "h")

saveArchRProject(proj, outputDirectory = "ArchRVignette", load = FALSE)
Adding pseudo-scRNA-seq profiles for each ATAC-seq cell
If we are happy with the integration results we add them to the Arrow Files. Then we can compare the linked gene expression with the inferred gene expression obtained through the gene scores.
#~5 minutes
proj <- addGeneIntegrationMatrix(
ArchRProj = proj,
useMatrix = "GeneScoreMatrix",
matrixName = "GeneIntegrationMatrix",
reducedDims = "IterativeLSI",
seRNA = seRNA,
addToArrow = TRUE,
force= TRUE,
groupList = groupList,
groupRNA = "BioClassification",
nameCell = "predictedCell",
nameGroup = "predictedGroup",
nameScore = "predictedScore",
)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
getAvailableMatrices(proj)
## [1] "GeneIntegrationMatrix" "GeneScoreMatrix" "TileMatrix"
proj <- addImputeWeights(proj)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes <- c(
"CD34", #Early Progenitor
"GATA1", #Erythroid
"PAX5", "MS4A1", #B-Cell Trajectory
"CD14", #Monocytes
"CD3D", "CD8A", "TBX21", "IL7R" #TCells
)
# gene expression values from GeneIntegrationMatrix
p1 <- plotEmbedding(
ArchRProj = proj,
colorBy = "GeneIntegrationMatrix",
name = markerGenes,
continuousSet = "horizonExtra",
embedding = "UMAP",
imputeWeights = getImputeWeights(proj)
)
# Gene score values from GeneScoreMatrix
p2 <- plotEmbedding(
ArchRProj = proj,
colorBy = "GeneScoreMatrix",
continuousSet = "horizonExtra",
name = markerGenes,
embedding = "UMAP",
imputeWeights = getImputeWeights(proj)
)
In the plot below you can see the gene expression values from integration with the scRNA-seq data.
p1c <- lapply(p1, function(x){
x + guides(color = FALSE, fill = FALSE) +
theme_ArchR(baseSize = 6.5) +
theme(plot.margin = unit(c(0, 0, 0, 0), "cm")) +
theme(
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank()
)
})
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
p2c <- lapply(p2, function(x){
x + guides(color = FALSE, fill = FALSE) +
theme_ArchR(baseSize = 6.5) +
theme(plot.margin = unit(c(0, 0, 0, 0), "cm")) +
theme(
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank()
)
})
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
do.call(cowplot::plot_grid, c(list(ncol = 3), p1c))

In the plot below you can see the gene score values which we calculate above.
do.call(cowplot::plot_grid, c(list(ncol = 3), p2c))

The results from the two different methdos for inferring gene expression are similar, but not identical.
saveArchRProject(proj, outputDirectory = "ArchRVignette", load = FALSE)
LS0tCnRpdGxlOiAiQXJjaFIgRmlyc3Qgc3RlcHMiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQoKCjxzdHlsZT4KYm9keSB7CnRleHQtYWxpZ246IGp1c3RpZnl9Cjwvc3R5bGU+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UsIGF1dG9kZXAgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICIvb21pY3MvZ3JvdXBzL09FMDUzMy9pbnRlcm5hbC9rYXRoYXJpbmEvc2NEb1JJL0FyY2hSIikKc2V0d2QoIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvQXJjaFIvIikKCnNldC5zZWVkKDEpCmBgYAoKCgpBcmNoUiB0YWtlcyBhcyBpbnB1dCBhbGlnbmVkIEJBTSBvciBmcmFnbWVudCBmaWxlcy4gVGhlc2UgZmlsZXMgYXJlIHN0b3JlZCBpbiB0aGUgCkhERjUgZmlsZSBmb3JtYXQgKGhpZXJhcmNoaWNhbCBkYXRhIGZvcm1hdCB2ZXJzaW9uNSkuIFRoZSBIREY1IGZpbGVzIGFyZSB0aGUgCmNvbnN0aXR1ZW50IHBpZWNlcyBvZiBhbiBBcmNoUiBhbmFseXNpcy4gVGhleSBhcmUgY2FsbGVkIEFycm93IGZpbGVzLiBBbGwgQXJyb3cKZmlsZXMgYXJlIGdyb3VwZWQgaW50byBhIHByb2plY3QsIGEgY29tcHJlc3NlZCBSIGRhdGEgZmlsZS4gVGhlIEZpbGVzIGFyZSBhY2Nlc3NlZCAKaW4gbWluaW1hbCBjaHVua3MgKHBhcmFsbGVsIHJlYWQgYW5kIHdyaXRlIG9wZXJhdGlvbnMpLiBUaGVyZWZvcmUsIGluIG1lbW9yeSB3ZQpkbyBub3QgaGF2ZSBhbnkgbGFyZ2UgZmlsZSBzaXplcy4KClRvIHNlbGVjdCBoaWdoIHF1YWxpdHkgY2VsbHMgVFNTIGVucmljaG1lbnQgc2NvcmVzIGFyZSB1c2VkLiAKCgogIAojIEZpcnN0IHN0ZXBzIGluIEFyY2hSCiAgCiMjIExvYWQgbGlicmFyaWVzCiAgCmBgYHtyfQpsaWJyYXJ5KEFyY2hSKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHJoZGY1KQojbGlicmFyeShjYXJldCkKaDVkaXNhYmxlRmlsZUxvY2tpbmcoKQpgYGAKICAKICAKYGBge3J9CmlucHV0RmlsZXMgPC0gZ2V0VHV0b3JpYWxEYXRhKCJIZW1hdG9wb2llc2lzIikKYGBgCgoKYGBge3J9CmFkZEFyY2hSR2Vub21lKCJoZzE5IikKYGBgCgojIyBDcmVhdGUgQXJyb3cgRmlsZXMKCjEuIHJlYWQgYWNjZXNzaWJsZSBmcmFnbWVudHMgCjIuIGNhbGN1bGF0ZSBRQyBpbmZvcm1hdGlvbiBmb3IgZWFjaCBjZWxsIChUU1MgZW5yaWNobWVudCBzY29yZXMgYW5kIG51Y2Vsb3NvbWUgaW5mbykKMy4gZmlsdGVyIGNlbGxzIGJhc2VkIG9uIFFDIHBhcmFtZXRlcnMKNC4gY3JlYXRlIGdlbm9tZS13aWRlIHRpbGUgbWF0cml4IHVzaW5nIDUwMC1icCBiaW5zCjUuIGNyZWF0ZSBHZW5lU2NvcmVNYXRyaXggdXNpbmcgZ2VuZSBhbm5vdGF0aW9uCgojIyMgVFNTIGVucmljaG1lbnQgc2NvcmUKPSBzaWduYWwgdG8gbm9pc2UgY2FsY3VsYXRpb24KVGhlIHJlYWRzIGFyb3VuZCBhIHBhcnRpY3VsYXIgVFNTIGFyZSBjb2xsZWN0ZWQgYW5kIGFnZ3JlZ2F0ZWQgdG8gZm9ybSBhIGRpc3RyaWJ1dGlvbgpvZiByZWFkcyB3aGljaCBpcyBjZW50ZXJlZCBvbiB0aCBUU1MuIHRoaXMgZGlzdHJpYnV0aW9uIGV4dGVuZHMgdG8gMTAwMGJwIGluIGJvdGggCmRpcmVjdGlvbnMKKiB3ZSB0YWtlIHRoZSBhdmVyYWdlIHJlYWQgZGVwdGggaW4gdGhlIDEwMCBicHMgYXQgZWFjaCBlbmQgZmxhbmsgYW5kIGNhbGN1YWx0ZSAKYSBmb2xkIGNoYW5nZSBhdCBlYWNoIHBvc2l0aW9uIG92ZXIgdGhhdCBhdmVyYWdlIHJlYWQgZGVwdGggKHRvdGFsIG9mIDIwMGJwIG9mIAphdmVyYWdlZCBkYXRhKQoqIElmIHRoZXJlIGlzIGEgaGlnaCByZWFkIHNpZ25hbCBhdCBUU1MgdGhlcmUgd2lsbCBiZSBhbiBpbmNyZWFzZSBpbiBzaWduYWwgdG93YXJkcwp0aGUgbWlkZGxlIG9mIHRoZSBkaXN0cmlidXRpb24KKiBoaWdoIHNpZ25hbCBtZWFucyB0aGF0IHRoZSByZWdpb24gaXMgYWNjZXNzaWJsZQoqIHRoZSBzaWduYWwgdmFsdWUgYXQgdGhlIGNlbnRlciBvZiB0aGUgZGlzdHJpYnV0aW9uIGlzIHRoZSBUU1MgZW5yaWNobWVudCBtZXRyaWMKCmBgYHtyfQpBcnJvd0ZpbGVzIDwtIGNyZWF0ZUFycm93RmlsZXMoCiAgaW5wdXRGaWxlcyA9IGlucHV0RmlsZXMsCiAgc2FtcGxlTmFtZXMgPSBuYW1lcyhpbnB1dEZpbGVzKSwKICBtaW5UU1MgPSA0LCAjRG9udCBzZXQgdGhpcyB0b28gaGlnaCBiZWNhdXNlIHlvdSBjYW4gYWx3YXlzIGluY3JlYXNlIGxhdGVyCiAgbWluRnJhZ3MgPSAxMDAwLCAjIG1pbmltdW0gbnVtYmVyIG9mIG1hcHBlZCBmcmFnbWVudHMgcmVxdWlyZWQKICBhZGRUaWxlTWF0ID0gVFJVRSwKICBhZGRHZW5lU2NvcmVNYXQgPSBUUlVFLAogIHN1YlRocmVhZGluZyA9IEZBTFNFCikKYGBgCgpgYGB7cn0KIyB0aGUgYXJyb3cgZmlsZXMgb2JqZWN0IHNpbXBseSBjb250YWlucyBhIGNoYXJhY3RlciB2ZWN0b3Igb2YgYXJyb3cgZmlsZSBwYXRocwpBcnJvd0ZpbGVzCmBgYAoKCiMjIFF1YWxpdHkgQ29udHJvbAoKMS4gdGhlIG51bWJlciBvZiAqKnVuaXF1ZSBudWNsZWFyIGZyYWdtZW50cyoqIChhcyBvcHBvc2VkIHRvIG1pdG9jaG9uZHJpYWwgZnJhZ21lbnRzKQoyLiAqKnNpZ25hbC10by1iYWNrZ3JvdW5kIHJhdGlvKiogLT4gaWYgdGhpcyBpcyBsb3cgdGhpcyBwcm9iYWJseSBjb3JyZXNwb25kcyB0byBkeWluZwpjZWxscyB3aGVyZSB0aGUgZW50aXJlIGdlbm9tZSBhbGxvd3MgcmFuZG9tIHRyYW5zcG9zaXRpb24KMy4gKipmcmFnbWVudCBzaXplIGRpc3RyaWJ1dGlvbioqIC0+IHNpbmNlIDE0NyBicCBhcmUgd3JhcHBlZCBhcm91bmQgYSBudWNsZW9zb21lIGl0IGlzIApleHBlY3RlZCB0aGF0IHRoZXJlIGFyZSBkZXBsZXRpb25zIG9mIGZyYWdtZW50cyBvZiB0aGlzIGxlbmd0aCBhdCByZWd1bGFyIGludGVydmFscwoKCiMjIEluZmVycmluZyBEb3VibGV0cwoKU2hvdWxkIGJlIHJlbW92ZWQsIGJlY2F1c2UgdGhleSBjYW4gaW50ZXJmZXJlIHdpdGggZG93bnN0cmVhbSBhbmFseXNpcy4KCioqRG91YmxldCBkZXRlY3Rpb24tYW5kLXJlbW92YWwgYWxnb3JpdGhtOioqCkhldGVyb3R5cGljIGRvdWJsZXRzIGFyZSBpZGVudGlmaWVkIGJ5IGdlbmVyYXRpbmcgYSBjb2xsZWN0aW9uIG9mIHN5bnRoZXRpYyBkb3VibGV0cy4KVGhlc2Ugc3ludGhldGljIGRvdWJsZXRzIGFyZSBwcm9qZWN0ZWQgaW50byBsb3ctZGltZW5zaW9uYWwgZW1iZWRkaW5ncy4gU2VhcmNoaW5nCmZvciBuZWFyZXN0IG5laWdoYm91cnMgdG8gdGhlIHN5bnRoZXRpYyBkb3VibGV0cyB3ZSBjYW4gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlCmRhdGFzZXQuIFRoaXMgb3V0cGVyZm9ybXMgdGhlIHByZWRpY3Rpb24gb2YgZG91YmxldHMgdXNpbmcgZnJhZ21lbnQgbnVtYmVyCihST0MtQVVDKS4gKENvbXBhcmVkIHRvIGRlbXV4bGV0IGFzIGdyb3VuZCB0cnV0aCkKCmBgYHtyfQpkb3ViU2NvcmVzIDwtIGFkZERvdWJsZXRTY29yZXMoCiAgaW5wdXQgPSBBcnJvd0ZpbGVzLAogIGsgPSAxMCwgI1JlZmVycyB0byBob3cgbWFueSBjZWxscyBuZWFyIGEgInBzZXVkby1kb3VibGV0IiB0byBjb3VudC4KICBrbm5NZXRob2QgPSAiVU1BUCIsICNSZWZlcnMgdG8gdGhlIGVtYmVkZGluZyB0byB1c2UgZm9yIG5lYXJlc3QgbmVpZ2hib3Igc2VhcmNoLgogIExTSU1ldGhvZCA9IDEKKQoKYGBgCgoKIyMgQ3JlYXRlIEFyY2hSUHJvamVjdAoKQW4gQXJjaFIgUHJvamVjdCBpcyBpbml0aWFsaXplZCB3aXRoIHNvbWUgaW1wb3J0YW50IGF0dHJpYnV0ZXM6CgoqIG91cHV0IGRpcmVjdG9yeQoqIHNhbXBsZSBuYW1lcwoqIGBzYW1wbGVDb2xEYXRhYCAtPiBtYXRyaXggY29udGFpbmludCBkYXRhIGZvciBlYWNoIHNhbXBsZQoqIGBjZWxsQ29sRGF0YWAgLT4gY29udGFpbnMgZGF0YSBhc3NvY2lhdGVkIHdpdGggZWFjaCBjZWxsCiAgKyBhZnRlciB1c2luZyBgYWRkRG91YmxldFNjb3JlKClgIHRoZXJlIHdpbGwgYmUgYSBjb2x1bW4gCiAgZm9yICJEb3VibGV0IEVucmljaG1lbnQiIGFuZCAiRG91YmxldCBTY29yZSIKKiB0b3RhbCBudW1iZXIgb2YgY2VsbHMgKGV4Y2x1ZGluZyBkb3VibGV0cykKKiBtZWRpYW4gVFNTIHNjb3JlICYgbWVkaWFuIG51bWJlciBvZiBmcmFnbWVudHMgYWNyb3NzIGFsbCBjZWxscyAKYW5kIHNhbXBsZXMKCmBgYHtyfQpwcm9qIDwtIEFyY2hSUHJvamVjdCgKICBBcnJvd0ZpbGVzID0gQXJyb3dGaWxlcywgCiAgb3V0cHV0RGlyZWN0b3J5ID0gIkFyY2hSVmlnbmV0dGUiLAogIGNvcHlBcnJvd3MgPSBUUlVFI1RoaXMgaXMgcmVjb21tZW5lZCBzbyB0aGF0IHlvdSBtYWludGFpbiBhbiB1bmFsdGVyZWQgY29weSBmb3IgbGF0ZXIgdXNhZ2UuCikKYGBgCgoqKkFjY2VzcyBBcmNoUjoqKgoKQ2VsbCBOYW1lczoKCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIGNlbGwgbmFtZXMKcHJvaiRjZWxsTmFtZXNbMTo1XQpgYGAKClNhbXBsZSBOYW1lczoKCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQpwcm9qJFNhbXBsZVsxOjVdCmxlbmd0aChwcm9qJFNhbXBsZSkKYGBgCgpGb3IgZWFjaCBjZWxsIHRoZXJlIGlzIGEgVFNTIGVucmljaG1lbnQgc2NvcmU6CgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KcHJvaiRUU1NFbnJpY2htZW50ICU+JSBoZWFkCnF1YW50aWxlKHByb2okVFNTRW5yaWNobWVudCkKYGBgCgojIyMjIyBIb3cgdG8gc3Vic2V0IGFuIEFyY2hSIHByb2plY3QKCiogbnVtZXJpY2FsbHkgCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBub3RlIHRoYXQgdGhhdCBvbmx5IDEwMCBjZWxscyBhcmUgYWNjZXNzZWQKcHJvalsxOjEwMCxdCmBgYAoKCiogYnkgY2VsbCBuYW1lCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KcHJvaltwcm9qJGNlbGxOYW1lc1sxOjEwMF0sIF0KYGBgCgoqIGtlZXAgc3BlY2lmaWMgc2FtcGxlcwoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CmlkeFNhbXBsZSA8LSBCaW9jR2VuZXJpY3M6OndoaWNoKHByb2okU2FtcGxlICVpbiUgInNjQVRBQ19CTU1DX1IxIikKY2VsbHNTYW1wbGUgPC0gcHJvaiRjZWxsTmFtZXNbaWR4U2FtcGxlXQpwcm9qW2NlbGxzU2FtcGxlLCBdCmBgYAoKKiBzcGVjaWZpYyBjdXRvZmYgZm9yIFRTUyBlbnJpY2htZW50IHNjb3JlCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KaWR4UGFzcyA8LSB3aGljaChwcm9qJFRTU0VucmljaG1lbnQgPj0gOCkKY2VsbHNQYXNzIDwtIHByb2okY2VsbE5hbWVzW2lkeFBhc3NdCnByb2pbY2VsbHNQYXNzLCBdCmBgYAoKCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIFdoYXQgaW5mb3JtYXRpb24gZG9lcyB0aGUgdmVjdG9yIG9mIFRTUyBlbnJpY2htZW50IGNvbnRhaW4/CmNsYXNzKHByb2okVFNTRW5yaWNobWVudCkKbGVuZ3RoKHByb2okVFNTRW5yaWNobWVudCkKbGVuZ3RoKHByb2okbkZyYWdzKQpgYGAKCgpgYGB7cn0KZ2V0QXZhaWxhYmxlTWF0cmljZXMocHJvaikKYGBgCgoKIyMjIE9idGFpbmluZyBjb2x1bW5zIGZyb20gY2VsbENvbERhdGEKCiogcmV0cmlldmUgbWV0YWRhdGEgY29sdW1ucywgZS5nLiBudW1iZXIgb2YgdW5pcXVlIG51Y2xlYXIgZnJhZ21lbnRzIHBlciBjZWxsCgpgYGB7ciwgIHJlc3VsdHMgPSAiYXNpcyJ9CmRmIDwtIGdldENlbGxDb2xEYXRhKHByb2osIHNlbGVjdCA9ICJuRnJhZ3MiKQpkZiAlPiUgaGVhZCAlPiUga2FibGUoKQpgYGAKCiogb3BlcmF0aW9ucyBvbiBhIGdpdmVuIGNvbHVtbgoKYGBge3IsICByZXN1bHRzID0gImFzaXMifQpkZiA8LSBnZXRDZWxsQ29sRGF0YShwcm9qLCBzZWxlY3QgPSBjKCJsb2cxMChuRnJhZ3MpIiwgIm5GcmFncyAtMSAiKSkKCmRmICU+JSBoZWFkICU+JSBrYWJsZSgpCmBgYAoKIyBQbG90IFFDIG1ldHJpY3MgCgojIyAtbG9nMTAodW5pcXVlIGZyYWdtZW50cykgdnMgVFNTIGVucmljaG1lbnQKCiogVFNTIGVucmljaG1lbnQgc2NvcmUgPSBzaWduYS10by1iYWNrZ3JvdW5kIAoqIG51bWJlciBvZiB1bmlxdWUgZnJhZ21lbnRzIC0+IGNlbGxzIHdpdGggdmVyeSBmZXcgZnJhZ21lbnRzIGRvIG5vdCBoYXZlIGVub3VnaCAKZGF0YSB0byBjb25maWRlbnRseSBhbmFseXplIHRoZW0gCiogaW4gdGhlIHBsb3QgYXJlYXMgd2l0aCBtb3JlIHBvaW50cy9jZWxscyBhcmUgY29sb3JlZCBpbiBvcmFuZ2UsIGFuZCBhcmVhcwp3aXRoIGxlc3MgcG9pbnRzIGluIGJsdWUsIGluZGljYXRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZWxsCmBgYHtyfQpkZiA8LSBnZXRDZWxsQ29sRGF0YShwcm9qLCBzZWxlY3QgPSBjKCJsb2cxMChuRnJhZ3MpIiwgIlRTU0VucmljaG1lbnQiKSkKZGYgJT4lIGhlYWQKYGBgCmBgYHtyfQpwIDwtIGdnUG9pbnQoCiAgeCA9IGRmWywgMV0sIHkgPSBkZlssIDJdLCAKICBjb2xvckRlbnNpdHkgPSBUUlVFLCAjIHNob3VsZCB0aGUgZGVuc2l0eSBvZiBwb2ludHMgb24gdGhlIHBsb3QgYmUgaW5kaWNhdGVkIGJ5IGNvbG9yPwogIGNvbnRpbnVvdXNTZXQgPSAic2FtYmFOaWdodCIsIAogIHhsYWJlbCA9ICJMb2cxMCB1bmlxdWUgZnJhZ21lbnRzIiwKICB5bGFiZWwgPSAiVFNTIGVucmljaG1lbnQiLAogIHhsaW0gPSBjKGxvZzEwKDUwMCksIHF1YW50aWxlKGRmWywxXSwgcHJvYnMgPSAwLjk5KSksCiAgeWxpbSA9IGMoMCwgcXVhbnRpbGUoZGZbLDJdLCBwcm9icyA9IDAuOTkpKQogICkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA0LCBsdHkgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDMsIGx0eSA9ICJkYXNoZWQiKQpwCmBgYAoKIyMjIFBsb3R0aW5nIHNhbXBsZSBzdGF0aXN0aWNzCgoqIHdoZW4gd2UgaGF2ZSBkaXN0aW5jdCBzYW1wbGVzLCBpdCBjYW4gYmUgaW1wb3J0YW50IHRvIGNvbXBhcmUgdmFyaW91cwptZXRyaWMgYmV0d2VlbiBzYW1wbGVzCiogcmlkZ2UgcGxvdHMgJiB2aW9saW4gcGxvdHMgYXJlIHVzZWQgZm9yIGdyb3VwZWQgZGF0YQoKYGBge3J9CnAxIDwtIHBsb3RHcm91cHMoCiAgQXJjaFJQcm9qID0gcHJvaiwKICBncm91cEJ5ID0gIlNhbXBsZSIsCiAgY29sb3JCeSA9ICJjZWxsQ29sRGF0YSIsCiAgbmFtZSA9ICJUU1NFbnJpY2htZW50IiwKICBwbG90QXMgPSAicmlkZ2VzIgopCgoKcDIgPC0gcGxvdEdyb3VwcygKICBBcmNoUlByb2ogPSBwcm9qLAogIGdyb3VwQnkgPSAiU2FtcGxlIiwKICBjb2xvckJ5ID0gImNlbGxDb2xEYXRhIiwKICBuYW1lID0gIlRTU0VucmljaG1lbnQiLAogIHBsb3RBcyA9ICJ2aW9saW4iLAogIGFscGhhID0gMC40LAogIGFkZEJveFBsb3QgPSBUUlVFCikKZ2dBbGlnblBsb3RzKHAxLCBwMiwgdHlwZSA9ICJoIikKCmBgYAoKYGBge3J9CnAxIDwtIHBsb3RHcm91cHMoCiAgQXJjaFJQcm9qID0gcHJvaiwKICBncm91cEJ5ID0gIlNhbXBsZSIsCiAgY29sb3JCeSA9ICJjZWxsQ29sRGF0YSIsCiAgbmFtZSA9ICJsb2cxMChuRnJhZ3MpIiwKICBwbG90QXMgPSAicmlkZ2VzIgopCgpwMiA8LSBwbG90R3JvdXBzKAogIEFyY2hSUHJvaiA9IHByb2osCiAgZ3JvdXBCeSA9ICJTYW1wbGUiLAogIGNvbG9yQnkgPSAiY2VsbENvbERhdGEiLAogIG5hbWUgPSAibG9nMTAobkZyYWdzKSIsCiAgcGxvdEFzID0gInZpb2xpbiIsCiAgYWxwaGE9IDAuNCwKICBhZGRCb3hQbG90ID0gVFJVRQopCgpnZ0FsaWduUGxvdHMocDEsIHAyLCB0eXBlID0gImgiKQpgYGAKCgojIyMgUGxvdCBGcmFnbWVudCBTaXplIERpc3RyaWJ1dGlvbiAmIFRTUyBFbnJpY2htZW50IFByb2ZpbGVzCgoqIHRoZSBkaXN0cmlidXRpb24gb2YgZnJhZ21lbnRzIHNpemUgY2FuIGJlIHZlcnkgZGlmZmVyZW50IGJldHdlZW4gc2FtcGxlcywKY2VsbCB0eXBlcyBhbmQgYmF0Y2hlcyAtPiB0aGVzZSBkaWZmZXJlbmNlcyBkbyBub3QgbmVjZXNzYXJpbHkgY29ycmVsYXRlIHdpdGggCmRpZmZlcmVuY2VzIGluIHF1YWxpdHkKKiB0aGUgZGlwIGlzIHRoZSBmcmFnbWVudCBzaXplIG9mIGEgbnVjbGVvc29tZSB+MTQ3YnAKKiBUU1MgZW5yaWNobWVudCBwcm9maWxlcwogICsgY2xlYXIgcGVhayBpbiB0aGUgY2VudGVyIAogICsgc21hbGxlciBzaG91bGRlciBwZWFrIHJpZ2h0IG9mIHRoZSBjZW50ZXIgY2F1c2VkIGJ5IHdlbGwgcG9zaXRpb25lZCArMSBudWNsZW9zb21lCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0KcDEgPC0gcGxvdEZyYWdtZW50U2l6ZXMoQXJjaFJQcm9qID0gcHJvaikKcDIgPC0gcGxvdFRTU0VucmljaG1lbnQoQXJjaFJQcm9qID0gcHJvaikKZ2dBbGlnblBsb3RzKHAxLCBwMiwgdHlwZSA9ICJ2IikKYGBgCgojIEZpbHRlcmluZyBEb3VibGV0cwoKV2l0aCB0aGUgZnVuY3Rpb24gYGFkZERvdWJsZVNjb3JlcygpYCBpbmZvcm1hdGlvbiBvbiBwcmVkaWN0ZWQgZG91YmxldHMgaGFzIGJlZW4KYWRkZWQuIEZpbHRlciB0aGUgcHV0YXRpdmUgZG91YmxldHMuIFRoZXkgYXJlIG5vdCByZW1vdmVkIHBoeXNpY2FsbHksIGJ1dCAKZXhjbHVkZWQgZnJvbSBkb3duc3RyZWFtIGFuYWx5c2lzLiBBcmNoUiBhdXRvbWF0aWNhbGx5IHByaW50cyB0aGUgbnVtYmVyIG9mCmNlbGxzIHJlbW92ZWQgZnJvbSBlYWNoIHNhbXBsZSBhbmQgdGhlIGNvcnJlc3BvbmRpbmcKcGVyY2VudGFnZSB3aGljaCBpcyB2ZXJ5IGhhbmR5LgoKKiphcmd1bWVudHM6KioKCiogY3V0RW5yaWNoID0gbWluaW11bSBjdXRvZmYgZm9yIERvdWJsZXRFbnJpY2htZW50LCBudW1iZXIgb2Ygc2ltdWxhdGVkIApkb3VibGV0cyBkaXZpZGVkIGJ5IGV4cGVjdGVkIG51bWJlciBnaXZlbiBhIHJhbmRvbSB1bmlmb3JtIGRpc3RyaWJ1dGlvbgoqIGN1dFNjb3JlID0gbWluaW11bSBjdXRvZmYgZm9yIERvdWJsZXQgU2NvcmUsIHJlcHJlc2VudHMgLWxvZzEwKGJpbm9taWFsIGFkanVzdGVkIHAtdmFsdWUpCmZvciB0aGUgRG91YmxldEVucmljaG1lbnRhZGQKKiBmaWx0ZVJhdGlvID0gbWF4aW11bSByYXRpbyBvZiBwcmVkaWN0ZWQgZG91YmxldHMgdG8gZmlsdGVyIGJhc2VkIG9uIG51bWJlciBvZiAKcGFzcy1maWx0ZXIgY2VsbHMgKEEgaGlnaGVyIGZpbHRlclJhdGlvIG1lYW5zIHRoYXQgbW9yZSBjZWxscyBhcmUgcmVtb3ZlZCkKZS5nLiA1MDAwIGNlbGxzCgptYXhpbXVtIHdvdWxkIGJlIGZpbHRlclJhdGlvICogNTAwMCAvIDEwMDAwMCA9IGZpbHRlclJhdGlvICogNTAwMCAqIDAuMDUKCioqVGhpcyB3YXkgc2FtcGxlcyB3aXRoIGRpZmZlcmVudCBwZXJjZW50YWdlIG9mIGRvdWJsZXRzIHdpbGwgYmUgZmlsdGVyZWQgYWNjb3JkaW5nbHkuKioKCmBgYHtyfQojIGluIG91ciBjYXNlIHdlIG5vdyBoYXZlIDEwIDI1MSBjZWxscyBhcyBvcHBvc2VkIHRvIDEwIDY2MSBjZWxscyBiZWZvcmUKIyBmaWx0ZXJpbmcgLT4gNDEwIGNlbGxzIHdlcmUgcmVtb3ZlZCAoMy44NSUpCnByb2ogPC0gZmlsdGVyRG91YmxldHMoQXJjaFJQcm9qID0gcHJvaikKYGBgCgoKIyBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gJiBDbHVzdGVyaW5nCgoqIHR3byBvdGhlciBhbGdvcml0aG1zOgogICsgbGF0ZW50IHNlbWFudGljIGluZGV4aW5nIChMU0kpIGluIFNpZ25hYwogICsgbGFuZG1hcmsgZGlmZnVzaW9uIG1hcHMgKExETSkgaW4gU25hcEFUQUMKICAKKiBBcmNoUjogb3B0aW1pemVkIGl0ZXJhdGl2ZSBMU0kgbWV0aG9kIAogICsgZXhoaWJpdHMgbGVzcyBzdXNjZXB0aWJpbGl0eSB0byBiYXRjaCBlZmZlY3RzIAogICsgZm9jdXNlcyBvbiBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIAogIDEuIGNyZWF0ZSBhIExTSSBSRWR1Y3Rpb24gZnJvbSBhIHN1YnNldCBvZiB0aGUgdG90YWwgY2VsbHMKICAyLiBsaW5lYXJseSBwcm9qZWN0IHRoZSByZW1haW5pbmcgY2VsbHMgaW50byB0aGlzIHN1YnNwYWNlIHdpdGggTFNJIHByb2VqZWN0aW9uCiAgKGJhc2VkIG9uIFNWRCkKCkJlY2F1c2Ugd2UgY2FuIGhhdmUgbWF4aW1hbGx5IHR3byBhY2Nlc3NpYmxlIGFsbGVsZXMgcGVyIGNlbGwsIHRoZSBzY0FUQUMtc2VxIGRhdGEKaXMgc3BhcnNlLiBUaGVyZWZvcmUsIHRoZSBtYWpvcml0eSBvZiBhY2Nlc3NpYmxlIHJlZ2lvbnMgYXJlIG5vdCB0cmFuc3Bvc2VkLCBtZWFuaW5nIAp0aGF0IG1vc3QgbG9jaSB3aWxsIGhhdmUgMCBhY2Nlc3NpYmxlIGFsbGVsZXMuIEEgemVybyBjb3VsZCBtZWFuICJub24tYWNjZXNzaWJsZSIgCm9yICJub3Qgc2FtcGxlZCIuIEZvciBtYW55IGFuYWx5c2lzIHdlIGNhbiB1c2UgYSBiaW5hcml6ZWQgbWF0aXguICoqSW1wb3JhbnRseSwqKgoqKnRoZSAxcyBoYXZlIGluZm9ybWF0aW9uLCBCVVQgdGhlIDBzIGRvIG5vdCEqKgoKQSBQQ0Egd291bGQgcmVzdWx0IGluIGhpZ2ggaW50ZXItY2VsbCBzaW1pbGFyaXR5IGF0IGFsbCAwIHBvc2l0aW9ucy4gQW4gYWx0ZXJuYXRpdmUKYXBwcm9hY2ggZm9yIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBpcyBhICoqbGF5ZXJlZCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24qKi4gRmlyc3QsCioqTGF0ZW50IFNlbWFudGljIEluZGV4aW5nIChMU0kpKiogaXMgdXNlZC4gTFNJIGlzIGFuIGFwcHJvYWNoIGZyb20gbGFuZ3VhZ2UKcHJvY2Vzc2luZy4gRGlmZmVyZW50IHNhbXBsZXMgYXJlIHRoZSAiZG9jdW1lbnRzIiBhbmQgZGlmZmVyZW50IHJlZ2lvbnMvcGVha3MgYXJlCnRoZSAid29yZHMiLiAKCiMjIEl0ZXJhdGl2ZSBMU0kKCjEuIGNvbXB1dGUgdGVybSBmcmVxdWVuY3kgKGRlcHRoIG5vcm1hbGl6YXRpb24gdG8gMTAsMDAwIHBlciBzaW5nbGUgY2VsbCkKCiRURiA9IFxmcmFje0Nfe2lqfX17Rl97an19JCB3aXRoICRDX3tpan0kIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgY291bnRzIGZvciBwZWFrIGkgaW4gY2VsbCBqIGFuZCAkRl97an0kIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgY291bnRzIGluIGNlbGwgai4KCjIuIEludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IAogICogd2VpZ2h0cyBmZWF0dXJlcyBieSBob3cgb2Z0ZW4gdGhleSBvY2N1ciAKICAqIG1vcmUgd2VpZ2h0IHRvIGxlc3MgZnJlcXVlbnQgcGVha3MKCiRJREYgPSBcZnJhY3tOfXtuX3twfX0kIHdpdGggTiBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIGluIHRoZSBkYXRhc2V0IGFuZCAkbl97cH0kIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgY291dG5zIGZvciBwZWFrIGkgYWNyb3NzIGFsbCBjZWxscy4KCjMuIFRoZSB0ZXJtIGZyZXF1ZW5jeSBURiBpcyBub3JtYWxpemVkIGJ5IHRoZSBpbnZlcnNlIGRvY3VtZW50IGZyZXF1bmN5IElERi4gWW91IGdldCBhICoqVEYtSURGKiogbWF0cml4ICh0ZXJtIGZyZXF1ZW5jeS1pbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSkgd2hpY2gKdGVsbHMgdXMgaG93IGltcG9ydGFudCBhIHJlZ2lvbi9wZWFrIGlzIHRvIGEgc2FtcGxlLiBJbiBvdGhlciB3b3JkcyB5b3UgdHJhbnNmb3JtIGEgYmluYXJ5IG1hdHJpeCB0byBhIG5vbi1iaW5hcnkgbWF0cml4LgoKJFRGLUlERiA9IFxsb2d7MSArIChURiAqIElERikgMTBeezR9fSQKCjQuIFNWRCBpZGVudGlmaWVzIHRoZSBtb3N0IHZhbHVhYmxlIGluZm9ybWF0aW9uIGFjcm9zcyBzYW1wbGVzLiBUaGVuIAp3ZSBjYW4gdXNlIHRoZXNlIG1vc3QgdmFsdWFibGUgZmVhdHVyZXMgdG8gcmVwcmVzZW50IHRoZSBkYXRhIGluIGEgbG93ZXIgZGltZW5zaW9uYWwgc3BhY2UKNS4gQ2x1c3RlcnMgYXJlIGlkZW50aWZpZWQgd2l0aCBTZXVyYXQncyBTaGFyZWQgTmVhcmVzdCBOZWlnaGJvciBjbHVzdGVyaW5nCjYuIFN1bSBhY2Nlc3NpYmlsaXR5IGFjcm9zcyBhbGwgc2luZ2xlIGNlbGxzIGluIGVhY2ggY2x1c3RlciAtPiBsb2ctbm9ybWFsaXplCjcuIElkZW50aWZ5IG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgYWNyb3NzIHRoZSBjbHVzdGVycwo4LiByZXBlYXQgd2l0aCBtb3N0IHZhcmlhYmxlIHBlYWtzIGFzIGZlYXR1cmVzCgpXaXRoIExTSSB3ZSBjYW4gcmVkdWNlIHRoZSBkaW1lbnNpb25hbGl0eSBvZiB0aGUgc3BhcnNlIGluc2VydGlvbiBtYXRyaXggdG8gdGVucyAKb3IgaHVuZHJlZHMuIFRoZW4gVU1BUCBvciB0LVNORSBjYW4gYmUgdXNlZCB0byB2aXN1YWxpemUgdGhlIGRhdGEKCgpVbmxpa2UgaW4gc2NSTkEtc2VxIHdlIGNhbm5vdCBzZWxlY3QgdGhlIHRvcCBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMgYmVmb3JlIApkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gKGhpZ2ggbm9pc2UsIGxvdyByZXByb2R1Y2liaWxpdHkpLiBSYXRoZXIgdGhlIGl0ZXJhdGl2ZSAKTFNJIGFwcHJvYWNoIGZpcnN0IGNvbXB1dGVzIGEgTFNJIApvbiB0aGUgbW9zdCBhY2Nlc3NpYmxlIHRpbGVzICh0aGlzIHdpbGwgaWRlbnRpZnkgY2x1c3RlcnMgY29ycmVzcG9uZGluZyB0byB0aGUgCm1ham9yIGNlbGwgdHlwZXMpLiBUaGVuLCBBcmNoUiBjb21wdXRlcyB0aGUgYXZlcmFnZSBhY2Nlc3NpYmlsaXR5IGFjcm9zcyB0aGVzZSAKY2x1c3RlcnMgYWNyb3NzIGFsbCBmZWF0dXJlcy4gTmV4dCwgdGhlIG1vc3QgdmFyaWFibGUgcGVha3MgYWNyb3NzIHRoZXNlIGNsdXN0ZXJzCmFyZSBpZGVudGlmaWVkLiBUaGUgbW9zdCBoaWdobHkgYWNjZXNzaWJsZSBwZWFrcyBhcmUgdGhlIGZlYXR1cmVzIG9mIGEgbmV3IApyb3VuZCBvZiBMU0kuIFdlIGNhbiBzZXQgaG93IG1hbnkgcm91bmRzIG9mIExTSSB3ZSB3YW50IHRvIGJlIHBlZm9ybWVkLiAKClVzaW5nIGl0ZXJhdGl2ZSBMU0kgcmVkdWNlcyBiYXRjaCBlZmZlY3RzLiBJZiB5b3Ugc2VlIHNvbWUgYmF0Y2ggZWZmZWN0cyB5b3UgY291bGQKdHJ5IHRvIGFkZCBtb3JlIExTSSBpdGVyYXRpb25zIGFuZCBzdGFydCBmcm9tIGEgbG93ZXIgaW50aXRpYWwgY2x1c3RlcmluZyAKcmVzb2x1dGlvbi4gQWxzbywgdGhlIG51bWJlciBvZiB2YXJpYWJsZSBmZWF0dXJlcyBjYW4gYmUgbG93ZXJlZC4gIAoKIyMgRXN0aW1hdGVkIExTSQoKKiBmb3IgZXh0cmVtZWx5IGxhcmdlIHNjQVRBQy1zZXEgZGF0YXNldHMKCjEuIHN1YnNldCBvZiBsYW5kbWFyayBjZWxscyAocmFuZG9tbHkgc2VsZWN0ZWQpIGFyZSB1c2VkIGZvciBMU0kKMi4gcmVtYWluaW5nIGNlbGxzIGFyZSBURi1JREYgbm9ybWxhaXplZCB1c2luZyB0aGUgSURGIGRldGVybWluZWQgZnJvbQp0aGUgbGFuZG1hcmsgY2VsbHMgZnJvbSBzdGVwIG9uZQozLiBub3JtYWxpemVkIGNlbGxzIGFyZSBwb3JqZWN0ZWQgaW50byB0aGUgU1ZEIHNwYWNlIGRlZmluZWQgYnkgdGhlIGxhbmRtYXJrIGNlbGxzCgpUaGUgTFNJIHRyYW5zZm9ybWF0aW9uIGlzLCB0aGVyZWZvcmUsIGJhc2VkIG9uIGEgc3Vic2V0IG9mIGNlbGxzIG9ubHkgYW5kIHRoZSAKcmVtYWluaW5nIGNlbGxzIGFyZSBwcm9qZWN0ZWQgaW50byB0aGlzIGxhbmRtYXJrIExTSS4gVGhpcyBhcHByb2FjaCBtaW5pbWl6ZXMgCm1lbW9yeSB1c2FnZS4gVGhlIGxhbmRtYXJrIHNldCBzaXplIGRlcGVuZHMgb24gY2VsbCB0eXBlIHByb3BvcnRpb25zLiAKCiogaXRlcmF0aXZlIExTSSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gLT4gZXhwbG9yZSB0aGUgcGFyYW1ldGVycyEKCiMjIEJhdGNoIGNvcnJlY3Qgd2l0aCBIYXJtb255CgoqIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBvYmplY3QgZnJvbSBBcmNoUiBpc3AgcGFzc2VkIHRvIEhhcm1vbnkKCmBgYHtyfQpwcm9qIDwtIGFkZEl0ZXJhdGl2ZUxTSShBcmNoUlByb2ogPSBwcm9qLCB1c2VNYXRyaXggPSAiVGlsZU1hdHJpeCIsIG5hbWUgPSAiSXRlcmF0aXZlTFNJIikKCmBgYAoKYGBgCnByb2ogPC0gYWRkSXRlcmF0aXZlTFNJKAogICAgQXJjaFJQcm9qID0gcHJvaiwKICAgIHVzZU1hdHJpeCA9ICJUaWxlTWF0cml4IiwgCiAgICBuYW1lID0gIkl0ZXJhdGl2ZUxTSSIsIAogICAgaXRlcmF0aW9ucyA9IDIsIAogICAgY2x1c3RlclBhcmFtcyA9IGxpc3QoICNTZWUgU2V1cmF0OjpGaW5kQ2x1c3RlcnMKICAgICAgICByZXNvbHV0aW9uID0gYygwLjIpLCAKICAgICAgICBzYW1wbGVDZWxscyA9IDEwMDAwLCAKICAgICAgICBuLnN0YXJ0ID0gMTAKICAgICksIAogICAgdmFyRmVhdHVyZXMgPSAyNTAwMCwgCiAgICBkaW1zVG9Vc2UgPSAxOjMwCikKYGBgCgojIyBDbHVzdGVyaW5nCgpDYWxsaW5nIGNsdXN0ZXJzIGluIHRoaXMgbmV3IHNwYWNlIHVzZXMgdGhlIFNldXJhdCdzIGdyYXBoIGNsdXN0ZXJpbmcgZnVuY3Rpb24gYXMgZGVmYXVsdCBjbHVzdGVyaW5nIG1ldGhvZC4gVGhlIFNldXJhdCAKbWV0aG9kIGZpcnN0IGNvbXB1dHMgS05OIGdyYXBoIGFuZCB0aGVuIGEgIG1vZHVsYXJpdHkgb3B0aW1pemF0aW9uCnRlY2huaXF1ZSB0byBjbHVzdGVyIHRoZSBjZWxscyAoaXRlcmF0aXZlbHkgZ3JvdXAgY2VsbHMgdG9nZXRldGhlcgp3aXRoIExvdXZpYW4gYWxnb3JpdGhtIHVzaW5nIDEwIHJhbmRvbSBzdGFydHMpLiBBbm90aGVyIG9wdGlvbiAKaXMgdG8gdXNlICJTY3JhbiIuIFRoZQpkZWZhdWx0IG51bWJlciBvZiBuZWFyZXN0IG5laWdoYm9ycyB1c2VkCmlzIDEwLiBUaGUgbWluaW11bSBudW1iZXIgb2YgY2VsbHMgZm9yIGEgY2x1c3RlciB0byBiZSBjYWxsZWQgYSAKY2x1c3RlciBpcyBzZXQgdG8gNSBieSBkZWZhdWx0LiBUaGUgbWF4aW11bSBudW1iZXIgb2YgY2x1c3RlcnMgdG8gCmJlIGNhbGxlZCBpcyBzZXQgdG8gMjUgYnkgZGVmYXVsdC4gCgpgYGB7cn0KcHJvaiA8LSBhZGRDbHVzdGVycyhpbnB1dCA9IHByb2osIHJlZHVjZWREaW1zID0gIkl0ZXJhdGl2ZUxTSSIpCmBgYAoKCiMgVmlzdWFsaXppbmcgYSBVTUFQIGVtYmVkZGluZwoKKiB1c2VzIHV3b3QgcGFja2FnZQoqIHZhcmlvdXMgYXR0cmlidXRlcyBvZiB0aGUgZGF0YSBjYW4gYmUgdmlzdWFsaXplZAogICsgdGhlc2UgYXJlIHN0b3JlZCBpbiBhIG1hdHJpeCBjYWxsZWQgYGNlbGxDb2xEYXRhYAogICsgd2hpY2ggdmFyaWFibGUgdGhlIHBsb3QgaXMgY29sb3JlZCBieSBpcyBzcGVjaWZpZWQgYnkgYGNvbG9yQnlgCiAgYW5kIGBuYW1lYCBwYXJhbWV0ZXIKCmBgYHtyfQpwcm9qIDwtIGFkZFVNQVAoQXJjaFJQcm9qID0gcHJvaiwgcmVkdWNlZERpbXMgPSAiSXRlcmF0aXZlTFNJIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTh9CiMgY29sb3IgYnkgc2FtcGxlCnAxIDwtIHBsb3RFbWJlZGRpbmcoQXJjaFJQcm9qID0gcHJvaiwgY29sb3JCeSA9ICJjZWxsQ29sRGF0YSIsIG5hbWUgPSAiU2FtcGxlIiwgZW1iZWRkaW5nID0gIlVNQVAiKQoKIyBjb2xvciBieSBjbHVzdGVyCnAyIDwtIHBsb3RFbWJlZGRpbmcoQXJjaFJQcm9qID0gcHJvaiwgY29sb3JCeSA9ICJjZWxsQ29sRGF0YSIsIG5hbWUgPSAiQ2x1c3RlcnMiLCBlbWJlZGRpbmcgPSAiVU1BUCIpCgpnZ0FsaWduUGxvdHMocDEsIHAyLCB0eXBlID0gImgiKQpgYGAKCiMgQ2x1c3RlciBhc3NpZ25tZW50IHVzaW5nIGdlbmUgc2NvcmVzCgpGb3IgdGhlIHRveSBkYXRhc2V0IG1hcmtlciBnZW5lcyBvZiBrbm93biBoZW1hdG9wb2lldGljIHJlZ3VsYXRvcnMKY2FuIGJlIHVzZWQuIFVzaW5nIE1BR0lDIHdlIGFkZCBpbXB1dGF0aW9uIHdlaWdodHMgdG8gc21vb3RoIHRoZSBkcm9wb3V0IG5vaXNlIGluIHRoZSBnZW5lIHNjb3JlcwoKYGBge3J9CnByb2ogPC0gYWRkSW1wdXRlV2VpZ2h0cyhwcm9qKQpgYGAKCmBgYHtyfQptYXJrZXJHZW5lcyAgPC0gYygKICAgICJDRDM0IiwgICNFYXJseSBQcm9nZW5pdG9yCiAgICAiR0FUQTEiLCAjRXJ5dGhyb2lkCiAgICAiUEFYNSIsICJNUzRBMSIsICJNTUUiLCAjQi1DZWxsIFRyYWplY3RvcnkKICAgICJDRDE0IiwgIk1QTyIsICNNb25vY3l0ZXMKICAgICJDRDNEIiwgIkNEOEEiI1RDZWxscwogICkKYGBgCgpgYGB7cn0KcCA8LSBwbG90RW1iZWRkaW5nKAogICAgQXJjaFJQcm9qID0gcHJvaiwgCiAgICBjb2xvckJ5ID0gIkdlbmVTY29yZU1hdHJpeCIsIAogICAgbmFtZSA9IG1hcmtlckdlbmVzLCAKICAgIGVtYmVkZGluZyA9ICJVTUFQIiwKICAgIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKcApgYGAKCgoKIyBWaXN1YWxpemluZyBHZW5vbWUgQnJvd3NlciBUcmFja3MKCkJyb3dzZSBsb2NhbCBjaHJvbWF0aW4gYWNjZXNzaWJpbGl0eSBhdCBtYXJrZXIgZ2VuZXMuIFBsb3QgZ2Vub21lCmJyb3dzZXIgdHJhY2tzIHBlciBjbHVzdGVyCmBgYHtyfQpwIDwtIHBsb3RCcm93c2VyVHJhY2soCiAgICBBcmNoUlByb2ogPSBwcm9qLCAKICAgIGdyb3VwQnkgPSAiQ2x1c3RlcnMiLCAKICAgIGdlbmVTeW1ib2wgPSBtYXJrZXJHZW5lcywgCiAgICB1cHN0cmVhbSA9IDUwMDAwLAogICAgZG93bnN0cmVhbSA9IDUwMDAwCikKYGBgCgoKYGBge3J9CmdyaWQ6OmdyaWQubmV3cGFnZSgpCmdyaWQ6OmdyaWQuZHJhdyhwJENEMTQpCmBgYAoKCgpgYGB7cn0Kc2F2ZUFyY2hSUHJvamVjdChwcm9qLCBvdXRwdXREaXJlY3RvcnkgPSAiQXJjaFJWaWduZXR0ZSIsIGxvYWQgPSBGQUxTRSkKYGBgCgoKIyBJbnRlZ3JhdGlvbiB3aXRoIHNjUk5BLXNlcQoKKiB0aGUgc2NBVEFDLXNlcSBnZW5lIHNjb3JlIG1hdHJpeCBpcyBjb21wYXJlZCB3aXRoIHRoZSBzY1JOQS1zZXEgZ2VuZSBleHByZXNzaW9uCm1hdHJpeAoqIGZvciB0aGlzIGFsaWdubWVudCB0aGUgYEZpbmRUcmFuc2ZlckFuY2hvcnMoKWAgZnVuY3Rpb24gZnJvbSBTZXVyYXQgaXMgdXNlZAoqIHRvIHNjYWxlIGZvciBsYXJnZSBzYW1wbGUgc2l6ZSwgdGhpcyBwcm9jZXNzIGlzIHBhcmFsbGVsaXplZAoqIGZvciBlYWNoIGNlbGwgaW4gQVRBQyB3ZSBmaW5kIHRoZSBjZWxsIGluIHNjUk5BLXNlcSB0aGF0IGxvb2tzIG1vc3Qgc2ltaWxhciAKLT4gYXNzaWduIHRoZSBjb3JyZXBzb25kaW5nIGdlbmUgZXhwcmVzc2lvbiB0byB0aGF0IGNlbGwuIAoKQXBhcnQgZnJvbSB1c2luZyB0aGlzIGluZm9ybWF0aW9uIGZvciBpZGVudGlmeWluZyBjbHVzdGVycyB3ZSBjYW4gYWxzbyB1c2UgaXQgCmZvciBpZGVudGlmeWluZyBwcmVkaWN0ZWQgY2lzLXJlZ3VsYXRvcnkgZWxlbWVudHMuCgoKYGBge3J9CmlmKCFmaWxlLmV4aXN0cygic2NSTkEtSGVtYXRvcG9pZXNpcy1HcmFuamEtMjAxOS5yZHMiKSl7CiAgICBkb3dubG9hZC5maWxlKAogICAgICAgIHVybCA9ICJodHRwczovL2plZmZncmFuamEuczMuYW1hem9uYXdzLmNvbS9BcmNoUi9UZXN0RGF0YS9zY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIsCiAgICAgICAgZGVzdGZpbGUgPSAic2NSTkEtSGVtYXRvcG9pZXNpcy1HcmFuamEtMjAxOS5yZHMiCiAgICApCn0KCiMgcmFuZ2VkIHN1bW1hcml6ZWQgRXhwZXJpbWVudApzZVJOQSA8LSByZWFkUkRTKCJzY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIpCnNlUk5BCmBgYAoKCmBgYHtyfQp0YWJsZShjb2xEYXRhKHNlUk5BKSRCaW9DbGFzc2lmaWNhdGlvbikKYGBgCgoKIyMgVW5jb25zdHJhaW5lZCAmIENvbnN0cmFpbmVkIGludGVncmF0aW9uCgojIyMgVW5jb25zdHJhaW5lZCBpbnRlZ3JhdGlvbjoKCiogdGFrZXMgYWxsIGNlbGxzIG9mIHNjQVRBQy1zZXEgZGF0YSBhbmQgYXR0ZW1wdHMgdG8gYWxpZ24gdGhlbSB0byBhbnkgb2YgdGhlIApzY1JOQS1zZXEgY2VsbHMKKiB0aGlzIHByb3ZpZGVzIHByZWxpbWluYXJ5IGNsdXN0ZXIgaWRlbnRpdGllcyB3aGljaCB3aWxsIHNlcnZlIGFzIHByaW9yIGtub3dsZWRnZQpmb3IgdGhlIGNvbnN0cmFpbmVkIGludGVncmF0aW9uCiogdGhlIHF1YWxpdHkgY2FuIGJlIGltcHJvdmVkIGJ5IGNvbnN0cmFpbmluZyB0aGUgaW50ZWdyYXRpb24KCgpUaGUgaW50ZWdyYXRpb24gbWF0cml4IHdpbGwgYmUgc3RvcmVkIGluIHRoZSBBcmNoUiBwcm9qZWN0IHZpYSBgbWF0cml4TmFtZWAuIFRoZSAKb3RoZXIgcGFyYW1ldGVycyB3aWxsIGJlIHNhdmVkIGluIHRoZSBjZWxsQ29sRGF0YSAobmFtZUNlbGwgPSBzdG9yZSBtYXRjaGVkIGNlbGwgSUQKZnJvbSBzY1JOQS1zZXEsIG5hbWVHcm91cCA9IHN0b3JlIGdyb3VwIElEIGZyb20gc2NSTkEtc2VxLCBuYW1lU2NvcmUgPSBzdG9yZSAKY3Jvc3MtcGxhdGZvcm0gaW50ZWdyYXRpb24gc2NvcmUpCgpgYGB7cn0KcHJvaiA8LSBhZGRHZW5lSW50ZWdyYXRpb25NYXRyaXgoCiAgICBBcmNoUlByb2ogPSBwcm9qLCAKICAgIHVzZU1hdHJpeCA9ICJHZW5lU2NvcmVNYXRyaXgiLAogICAgbWF0cml4TmFtZSA9ICJHZW5lSW50ZWdyYXRpb25NYXRyaXgiLAogICAgcmVkdWNlZERpbXMgPSAiSXRlcmF0aXZlTFNJIiwKICAgIHNlUk5BID0gc2VSTkEsCiAgICBhZGRUb0Fycm93ID0gRkFMU0UsCiAgICBncm91cFJOQSA9ICJCaW9DbGFzc2lmaWNhdGlvbiIsICMgQmlvY2xhc3NpZmljYXRpb24gaXMgYSBjb2x1bW4gaW4KICAgICMgdGhlIGNvbERhdGEgb2YgdGhlIHNlUk5BIG9iamVjdCwgdGhpcyBjb2x1bW4gd2lsbCBiZSB1c2VkIHRvIAogICAgIyBkZXRlcm1pbmUgdGhlIHN1Ymdyb3VwaW5ncyBzcGVjaWZpZWQgaW4gZ3JvdXBMaXN0IGZvciBjb25zdHJhaW5lZCBpbnRlZ3JhdGlvbgogICAgIyBpdCBpcyBhbHNvIHVzZWQgZm9yIHRoZSBuYW1lR3JvdXAgb3V0cHV0IG9mIHRoaXMgZnVuY3Rpb24KICAgIG5hbWVDZWxsID0gInByZWRpY3RlZENlbGxfVW4iLCAjIG5hbWUgdGhlIGNlbGxDb2xEYXRhIGZvciB0aGUgCiAgICAjIHByZWRpY3RlZCBzY1JOQS1zZXEgY2VsbCBpbiB0aGUgc3BlY2lmaWVkIEFyY2hSUHJvamVjdCAtPgogICAgIyB3aWxsIGFkZCBhIGNvbHVtbiB0byB0aGUgcHJvamVjdAogICAgbmFtZUdyb3VwID0gInByZWRpY3RlZEdyb3VwX1VuIiwKICAgIG5hbWVTY29yZSA9ICJwcmVkaWN0ZWRTY29yZV9VbiIKKQpgYGAKCgoKIyMjIENvbnN0cmFpbmVkIGludGVncmF0aW9uOgoKKiBwcmlvciBrbm93bGVkZ2Ugb2YgdGhlIGNlbGwgdHlwZXMgaXMgdXNlZCB0byBsaW1pdCB0aGUgc2VhcmNoIHNwYWNlIG9mIHRoZSBhbGlnbm1lbnQKLT4gZS5nLiBpZiB3ZSBrbm93IHRoYXQgY2x1c3RlcnMgQSwgQiwgQyBpbiBzY0FUQUMtc2VxIGRhdGEgY29ycmVzcG9uZCB0byAzCmRpZmZlcmVudCBUIGNlbGwgY2x1c3RlcnMgYW5kIENsdXN0ZXJzIFgsIFkgaW4gc2NSTkEtc2VxIGRhdGEgY29ycmVzcG9uZCB0byAKdHdvIFQgY2VsbCBjbHVzdGVycywgd2UgY291bGQgdHJ5IHRvIHNwZWNpZmljYWxseSBhbGlnbiB0aGVzZSBjZWxscwoKMS4gSWRlbnRpZnkgd2hpY2ggY2VsbCB0eXBlcyBmcm9tIHNjUk5BLXNlcSBkYXRhIGFyZSBtb3N0IGFidW5kYW50IGluIHNjQVRBQy1zZXEgY2x1c3RlcnM/CjIuIGNvbmZ1c2lvbiBtYXRyaXggaXMgY3JlYXRlZCB0aGF0IGxvb2tzIGF0IHRoZSBpbnRlcnNlY3Rpb24gb2YgQ2x1c3RlcnMgCmFuZCBwcmVkaWN0ZWQgZ3JvdXAgd2hpY2ggY29udGFpbnMgdGhlIGNlbGwgdHlwZXMgYXMgaWRlbnRmaWVkIGJ5IHNjUk5BLXNlcQozLiBUaGlzIHNob3dzIHdoaWNoIHNjUk5BLXNlcSBjZWxsIHR5cGUgaXMgbW9zdCBhYnVuZGFudCBpbiBlYWNoIG9mIHRoZSBzY0FUQUMgY2x1c3RlcnMKCmBgYHtyfQojIGluIHRoZSBjb25mdXNpb24gbWF0cml4IHRoZSByb3dzIGNvcnJlc3BvbmQgdG8gY2x1c3RlcnMgaW4gc2NBVEFDIGRhdGEKIyB0aGUgY29sdW1ucyBjb3JyZXNwb25kIHRvIGNlbGwgdHlwZXMgaW4gc2NSTkEtc2VxIGRhdGEKY00gPC0gYXMubWF0cml4KGNvbmZ1c2lvbk1hdHJpeChwcm9qJENsdXN0ZXJzLCBwcm9qJHByZWRpY3RlZEdyb3VwX1VuKSkKY01bLCAxOjVdCgpwcmVDbHVzdCA8LSBjb2xuYW1lcyhjTSlbYXBwbHkoY00sIDEgLCB3aGljaC5tYXgpXQpjYmluZChwcmVDbHVzdCwgcm93bmFtZXMoY00pKSAjQXNzaWdubWVudHMKYGBgCgoKCldoaWNoIGNlbGxzIGluIHRoZSBzY1JOQS1zZXEgY29ycmVzcG9uZCB0byBUY2VsbHMgYW5kIE5LIGNlbGxzPyAKClVzZSBwYXR0ZXJuIG1hdGNoaW5nIHN0cmluZ3MgaW4gY29tYmluYXRpb24gd2l0aCBncmVwIHRvIGV4dHJhY3QgdGhlIHNjQVRBQy1zZXEgCmNsdXN0ZXJzIHRoYXQgY29ycmVzcG9uZCB0byB0aGVzZSBzY1JOQS1zZXEgY2VsbCB0eXBlcy4KfCBhY3RzIGFzIGFuIG9yIHN0YXRlbWVudCwgc28gd2Ugc2VhcmNoIGZvciBhbnkgcm93IGluIHRoZSBwcmVDbHVzdCBjb2x1bW4gb2YgdGhlCmNvbmZ1c2lvbiBtYXRpeCB0aGF0IG1hdGNoZXMgYSBzY1JOQS1zZXEgY2x1c3RlciBudW1iZXIuCgpgYGB7cn0KdW5pcXVlKHByb2okcHJlZGljdGVkR3JvdXBfVW4pCmBgYAoKYGBge3J9CmNUTksgPC0gcGFzdGUwKHBhc3RlMCgxOToyNSksIGNvbGxhcHNlID0gInwiKQpjVE5LCgpjTm9uVE5LIDwtIHBhc3RlMChjKHBhc3RlMCgiMCIsIDE6OSksIDEwOjEzLCAxNToxOCksIGNvbGxhcHNlID0gInwiKQpjTm9uVE5LCmBgYAoKYGBge3J9CmNsdXN0VE5LIDwtIHJvd25hbWVzKGNNKVtncmVwKGNUTkssIHByZUNsdXN0KV0KY2x1c3RUTksKCmNsdXN0Tm9uVE5LIDwtIHJvd25hbWVzKGNNKVtncmVwKGNOb25UTkssIHByZUNsdXN0KV0KY2x1c3ROb25UTksKYGBgCgpJZGVudGlmeSBzY1JOQS1zZXEgY2VsbHMgY29ycmVzcG9uZGluZyB0byB0aGUgc2FtZSBjZWxsIHR5cGVzLgoKYGBge3J9CiNSTkEgZ2V0IGNlbGxzIGluIHRoZXNlIGNhdGVnb3JpZXMKcm5hVE5LIDwtIGNvbG5hbWVzKHNlUk5BKVtncmVwKGNUTkssIGNvbERhdGEoc2VSTkEpJEJpb0NsYXNzaWZpY2F0aW9uKV0KaGVhZChybmFUTkspCgojIGdldCBjZWxsIG5vdCBpbiB0aGVzZSBjYXRlZ29yaWVzCnJuYU5vblROSyA8LSBjb2xuYW1lcyhzZVJOQSlbZ3JlcChjTm9uVE5LLCBjb2xEYXRhKHNlUk5BKSRCaW9DbGFzc2lmaWNhdGlvbildCmhlYWQocm5hTm9uVE5LKQpgYGAKCgpXZSBjcmVhdGUgYSBuZXN0ZWQgbGlzdCB3aXRoIHR3byB2ZWN0b3JzIG9mIGNlbGwgSURzCmBgYHtyfQpncm91cExpc3QgPC0gU2ltcGxlTGlzdCgKICAgIFROSyA9IFNpbXBsZUxpc3QoCiAgICAgICAgQVRBQyA9IHByb2okY2VsbE5hbWVzW3Byb2okQ2x1c3RlcnMgJWluJSBjbHVzdFROS10sCiAgICAgICAgUk5BID0gcm5hVE5LCiAgICApLAogICAgTm9uVE5LID0gU2ltcGxlTGlzdCgKICAgICAgICBBVEFDID0gcHJvaiRjZWxsTmFtZXNbcHJvaiRDbHVzdGVycyAlaW4lIGNsdXN0Tm9uVE5LXSwKICAgICAgICBSTkEgPSBybmFOb25UTksKICAgICkgICAgCikKCmdyb3VwTGlzdFtbMV1dW1sxXV0gJT4lIGhlYWQKYGBgCgpgYGB7cn0KcHJvaiA8LSBhZGRHZW5lSW50ZWdyYXRpb25NYXRyaXgoCiAgICBBcmNoUlByb2ogPSBwcm9qLCAKICAgIHVzZU1hdHJpeCA9ICJHZW5lU2NvcmVNYXRyaXgiLAogICAgbWF0cml4TmFtZSA9ICJHZW5lSW50ZWdyYXRpb25NYXRyaXgiLAogICAgcmVkdWNlZERpbXMgPSAiSXRlcmF0aXZlTFNJIiwKICAgIHNlUk5BID0gc2VSTkEsCiAgICBhZGRUb0Fycm93ID0gRkFMU0UsIAogICAgZ3JvdXBMaXN0ID0gZ3JvdXBMaXN0LAogICAgZ3JvdXBSTkEgPSAiQmlvQ2xhc3NpZmljYXRpb24iLAogICAgbmFtZUNlbGwgPSAicHJlZGljdGVkQ2VsbF9DbyIsCiAgICBuYW1lR3JvdXAgPSAicHJlZGljdGVkR3JvdXBfQ28iLAogICAgbmFtZVNjb3JlID0gInByZWRpY3RlZFNjb3JlX0NvIgopCmBgYAoKCmBgYHtyfQpwYWwgPC0gcGFsZXR0ZURpc2NyZXRlKHZhbHVlcyA9IGNvbERhdGEoc2VSTkEpJEJpb0NsYXNzaWZpY2F0aW9uKQpwYWwKYGBgCgojIyMgQ29tcGFyZSB1bmNvbnN0cmFpbmVkIGFuZCBjb25zdHJhaW5lZCBpbnRlZ3JhdGlvbgoKUGxvdCB0aGUgaW50ZWdyYXRpb24gZm9yIHVuY29uc3RyYWluZWQgKGxlZnQpIGFuZCBjb25zdHJhaW5lZCAocmlnaHQpCmludGVncmF0aW9uLiBXZSBvdmVybGF5IHRoZSBzY1JOQS1zZXEgY2VsbCB0eXBlcyBvbiB0aGUgQVRBQy1zZXEgZGF0YQpiYXNlZCBvbiB0aGUgdW5jb25zdHJhaW5lZC9jb25zdHJhaW5lZCBpbnRlZ3JhdGlvbi4gSW4gdGhlIHBsb3QgYmVsb3cgdGhlIGRpZmZlcmVuY2UgaXMgdmVyeSBzdWJ0bGUsIGJlY2F1c2UgdGhlIGNlbGwgdHlwZXMgb2YgaW50ZXJlc3QgYXJlIHZlcnkgZGlzdGluY3QuIFRoZXJlIGFyZSBzdWJ0bGUgZGlmZmVyZW5jZXMsIGZvciBleGFtcGxlIGluIHRoZSBUIGNlbGwgY2x1c3RlcnMuIENvdWxkIHdlIGFsc28gdHJ5IHRoaXMgYXBwcm9hY2ggd2l0aCBjb25zdHJhaW5pbmcgb3RoZXIgY2VsbCB0eXBlcz8gQ2FuIHdlIGNvbWJpbmUgZGlmZmVyZW50IGNvbnN0cmFpbnMvZ3JvdXBzPwoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KcDEgPC0gcGxvdEVtYmVkZGluZygKICAgIHByb2osIAogICAgY29sb3JCeSA9ICJjZWxsQ29sRGF0YSIsIAogICAgbmFtZSA9ICJwcmVkaWN0ZWRHcm91cF9VbiIsIAogICAgcGFsID0gcGFsCikKCgpwMiA8LSBwbG90RW1iZWRkaW5nKAogICAgcHJvaiwgCiAgICBjb2xvckJ5ID0gImNlbGxDb2xEYXRhIiwgCiAgICBuYW1lID0gInByZWRpY3RlZEdyb3VwX0NvIiwgCiAgICBwYWwgPSBwYWwKKQoKCmdnQWxpZ25QbG90cyhwMSwgcDIsIHR5cGUgPSAiaCIpCmBgYAoKYGBge3J9CnNhdmVBcmNoUlByb2plY3QocHJvaiwgb3V0cHV0RGlyZWN0b3J5ID0gIkFyY2hSVmlnbmV0dGUiLCBsb2FkID0gRkFMU0UpCmBgYAoKIyMgQWRkaW5nIHBzZXVkby1zY1JOQS1zZXEgcHJvZmlsZXMgZm9yIGVhY2ggQVRBQy1zZXEgY2VsbAoKSWYgd2UgYXJlIGhhcHB5IHdpdGggdGhlIGludGVncmF0aW9uIHJlc3VsdHMgd2UgYWRkIHRoZW0gdG8gdGhlIEFycm93IEZpbGVzLiBUaGVuIHdlIGNhbiBjb21wYXJlIHRoZSBsaW5rZWQgZ2VuZSBleHByZXNzaW9uIHdpdGggdGhlIGluZmVycmVkIGdlbmUgZXhwcmVzc2lvbiBvYnRhaW5lZCB0aHJvdWdoIHRoZSBnZW5lIHNjb3Jlcy4gCgpgYGB7cn0KI341IG1pbnV0ZXMKcHJvaiA8LSBhZGRHZW5lSW50ZWdyYXRpb25NYXRyaXgoCiAgQXJjaFJQcm9qID0gcHJvaiwgCiAgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIsCiAgbWF0cml4TmFtZSA9ICJHZW5lSW50ZWdyYXRpb25NYXRyaXgiLAogIHJlZHVjZWREaW1zID0gIkl0ZXJhdGl2ZUxTSSIsCiAgc2VSTkEgPSBzZVJOQSwKICBhZGRUb0Fycm93ID0gVFJVRSwKICBmb3JjZT0gVFJVRSwKICBncm91cExpc3QgPSBncm91cExpc3QsCiAgZ3JvdXBSTkEgPSAiQmlvQ2xhc3NpZmljYXRpb24iLAogIG5hbWVDZWxsID0gInByZWRpY3RlZENlbGwiLAogIG5hbWVHcm91cCA9ICJwcmVkaWN0ZWRHcm91cCIsCiAgbmFtZVNjb3JlID0gInByZWRpY3RlZFNjb3JlIiwKICAKKQoKCmdldEF2YWlsYWJsZU1hdHJpY2VzKHByb2opCgpwcm9qIDwtIGFkZEltcHV0ZVdlaWdodHMocHJvaikKYGBgCgoKCmBgYHtyfQptYXJrZXJHZW5lcyAgPC0gYygKICAiQ0QzNCIsICNFYXJseSBQcm9nZW5pdG9yCiAgIkdBVEExIiwgI0VyeXRocm9pZAogICJQQVg1IiwgIk1TNEExIiwgI0ItQ2VsbCBUcmFqZWN0b3J5CiAgIkNEMTQiLCAjTW9ub2N5dGVzCiAgIkNEM0QiLCAiQ0Q4QSIsICJUQlgyMSIsICJJTDdSIiAjVENlbGxzCikKCiMgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmcm9tIEdlbmVJbnRlZ3JhdGlvbk1hdHJpeApwMSA8LSBwbG90RW1iZWRkaW5nKAogIEFyY2hSUHJvaiA9IHByb2osIAogIGNvbG9yQnkgPSAiR2VuZUludGVncmF0aW9uTWF0cml4IiwgCiAgbmFtZSA9IG1hcmtlckdlbmVzLCAKICBjb250aW51b3VzU2V0ID0gImhvcml6b25FeHRyYSIsCiAgZW1iZWRkaW5nID0gIlVNQVAiLAogIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKCgojIEdlbmUgc2NvcmUgdmFsdWVzIGZyb20gR2VuZVNjb3JlTWF0cml4CgpwMiA8LSBwbG90RW1iZWRkaW5nKAogIEFyY2hSUHJvaiA9IHByb2osIAogIGNvbG9yQnkgPSAiR2VuZVNjb3JlTWF0cml4IiwgCiAgY29udGludW91c1NldCA9ICJob3Jpem9uRXh0cmEiLAogIG5hbWUgPSBtYXJrZXJHZW5lcywgCiAgZW1iZWRkaW5nID0gIlVNQVAiLAogIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKYGBgCgpJbiB0aGUgcGxvdCBiZWxvdyB5b3UgY2FuIHNlZSB0aGUgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmcm9tCmludGVncmF0aW9uIHdpdGggdGhlIHNjUk5BLXNlcSBkYXRhLgoKYGBge3J9CnAxYyA8LSBsYXBwbHkocDEsIGZ1bmN0aW9uKHgpewogIHggKyBndWlkZXMoY29sb3IgPSBGQUxTRSwgZmlsbCA9IEZBTFNFKSArIAogICAgdGhlbWVfQXJjaFIoYmFzZVNpemUgPSA2LjUpICsKICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsIDAsIDAsIDApLCAiY20iKSkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIAogICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIAogICAgICBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpCiAgICApCn0pCgpwMmMgPC0gbGFwcGx5KHAyLCBmdW5jdGlvbih4KXsKICB4ICsgZ3VpZGVzKGNvbG9yID0gRkFMU0UsIGZpbGwgPSBGQUxTRSkgKyAKICAgIHRoZW1lX0FyY2hSKGJhc2VTaXplID0gNi41KSArCiAgICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLCAwLCAwLCAwKSwgImNtIikpICsKICAgIHRoZW1lKAogICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIAogICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKQogICAgKQp9KQoKZG8uY2FsbChjb3dwbG90OjpwbG90X2dyaWQsIGMobGlzdChuY29sID0gMyksIHAxYykpCmBgYAoKSW4gdGhlIHBsb3QgYmVsb3cgeW91IGNhbiBzZWUgdGhlIGdlbmUgc2NvcmUgdmFsdWVzIHdoaWNoIHdlIGNhbGN1bGF0ZQphYm92ZS4KCmBgYHtyfQpkby5jYWxsKGNvd3Bsb3Q6OnBsb3RfZ3JpZCwgYyhsaXN0KG5jb2wgPSAzKSwgcDJjKSkKYGBgCgoKVGhlIHJlc3VsdHMgZnJvbSB0aGUgdHdvIGRpZmZlcmVudCBtZXRoZG9zIGZvciBpbmZlcnJpbmcgZ2VuZSBleHByZXNzaW9uIGFyZSBzaW1pbGFyLCBidXQgbm90IGlkZW50aWNhbC4gCgoKYGBge3J9CnNhdmVBcmNoUlByb2plY3QocHJvaiwgb3V0cHV0RGlyZWN0b3J5ID0gIkFyY2hSVmlnbmV0dGUiLCBsb2FkID0gRkFMU0UpCmBgYA==